This repository has been archived on 2026-08-23. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
pi-web/src/server/sessions/sessionService.ts
T
Federico Jaramillo Martinez 51ebfe4c00 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.
2026-07-26 22:38:15 +02:00

85 lines
4.8 KiB
TypeScript

import type {
AskUserCloseResponse,
AskUserSubmission,
SavedPromptAttachment,
SessionBulkArchiveResponse,
SessionBulkDeleteArchivedResponse,
SessionBulkMutationRef,
SessionNotificationCatalogSnapshot,
SessionNotificationDismissAllRequest,
SessionNotificationDismissRequest,
SessionNotificationInboxSnapshot,
SessionUnreadAcknowledgeRequest,
SessionUnreadCatalogSnapshot,
} from "../../shared/apiTypes.js";
import type {
ClientArchiveSessionsResponse,
ClientCommand,
ClientCommandResult,
ClientMessagePage,
ClientSession,
ClientSessionCleanupExecuteResponse,
ClientSessionCleanupPreviewResponse,
ClientSessionModel,
ClientSessionRef,
ClientSessionStatus,
ClientSessionTreeNavigateRequest,
ClientSessionTreeNavigateResult,
ClientThinkingLevel,
SessionStreamSnapshot,
} from "../types.js";
import type { NormalizedSessionCleanupRequest } from "./sessionCleanup.js";
export type SessionRouteRef = ClientSessionRef;
export type SessionRouteLookup = string | SessionRouteRef;
/**
* Route-facing session contract for PI WEB's HTTP/WebSocket API.
*
* Keep transport concerns separate from the bundled Pi SDK implementation so
* routes remain testable. Pi-specific lifecycle hooks such as auth-change
* handling and daemon shutdown stay on the concrete service.
*/
export interface SessionRouteService {
list(cwd: string): Promise<ClientSession[]>;
start(cwd: string): Promise<ClientSession>;
messages(ref: SessionRouteLookup, page?: { before?: number; limit?: number }): Promise<unknown[] | ClientMessagePage>;
status(ref: SessionRouteLookup): Promise<ClientSessionStatus>;
streamSnapshot(ref: SessionRouteLookup): Promise<SessionStreamSnapshot>;
notificationCatalog(): SessionNotificationCatalogSnapshot | Promise<SessionNotificationCatalogSnapshot>;
unreadCatalog(): Promise<SessionUnreadCatalogSnapshot>;
acknowledgeUnread(sessionId: string, request: SessionUnreadAcknowledgeRequest): Promise<SessionUnreadCatalogSnapshot>;
notificationInbox(ref: SessionRouteRef): SessionNotificationInboxSnapshot | Promise<SessionNotificationInboxSnapshot>;
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>;
cycleModel(ref: SessionRouteLookup, direction: "forward" | "backward"): Promise<ClientSessionStatus>;
availableThinkingLevels(ref: SessionRouteLookup): Promise<ClientThinkingLevel[]>;
setThinkingLevel(ref: SessionRouteLookup, level: string): Promise<ClientSessionStatus>;
cycleThinkingLevel(ref: SessionRouteLookup): Promise<ClientSessionStatus>;
commands(ref: SessionRouteLookup): Promise<ClientCommand[]>;
prompt(ref: SessionRouteLookup, text: unknown, streamingBehavior?: unknown, attachments?: unknown): Promise<void>;
saveAttachments(ref: SessionRouteLookup, attachments: unknown, folder?: string): Promise<SavedPromptAttachment[]>;
cleanupPreview(request: NormalizedSessionCleanupRequest): Promise<ClientSessionCleanupPreviewResponse>;
cleanup(request: NormalizedSessionCleanupRequest): Promise<ClientSessionCleanupExecuteResponse>;
archiveMany(refs: readonly SessionBulkMutationRef[]): Promise<SessionBulkArchiveResponse>;
deleteArchivedMany(refs: readonly SessionBulkMutationRef[]): Promise<SessionBulkDeleteArchivedResponse>;
shell(ref: SessionRouteLookup, text: string): Promise<void>;
runCommand(ref: SessionRouteLookup, text: string): Promise<ClientCommandResult>;
respondToCommand(ref: SessionRouteLookup, requestId: string, value: string): Promise<ClientCommandResult>;
navigateTree(ref: SessionRouteLookup, request: ClientSessionTreeNavigateRequest): Promise<ClientSessionTreeNavigateResult>;
abort(ref: SessionRouteLookup): Promise<void>;
stop(ref: SessionRouteLookup): void | Promise<void>;
archive(ref: SessionRouteLookup): Promise<void>;
archiveTree(ref: SessionRouteLookup): Promise<ClientArchiveSessionsResponse>;
restore(ref: SessionRouteLookup): Promise<void>;
deleteArchived(ref: SessionRouteLookup): Promise<void>;
reload(ref: SessionRouteLookup): Promise<void>;
detachParent(ref: SessionRouteLookup): Promise<void>;
}