Skip to content

Generated flows

Every node below is read out of the source the flow runs through — the route table in packages/web/src/server/index.ts and the EnsoObservation union in packages/core/src/observation.ts. Nothing here is drawn by hand, so a route or a kind that moves changes this page or fails the docs gate.

The 12 control routes: what the browser can ask the runtime to do. Observation routes are the next diagram’s first stage rather than a second copy of it.

flowchart LR
  Browser["Browser"]
  Browser -->|POST /api/projects| R_POST_api_projects["respondToProjectRegistration → ProjectRegistry.register"]
  Browser -->|DELETE /api/projects/:projectId| R_DELETE_api_projects_projectId["respondToProjectRemoval → ProjectRegistry.remove"]
  Browser -->|DELETE /api/prompts/:id| R_DELETE_api_prompts_id["respondToPromptRemoval → PromptLibrary.remove"]
  Browser -->|PUT /api/prompts/:id| R_PUT_api_prompts_id["respondToPromptWrite → PromptLibrary.write"]
  Browser -->|POST /api/threads/:threadId/abort| R_POST_api_threads_threadId_abort["respondToAbort → abortThread"]
  R_POST_api_threads_threadId_abort --> H_respondToAbort_abortThread["abortThread()"]
  H_respondToAbort_abortThread --> Runtime
  Browser -->|POST /api/threads/:threadId/command| R_POST_api_threads_threadId_command["respondToHostCommand → runHostCommand"]
  Browser -->|POST /api/threads/:threadId/configure| R_POST_api_threads_threadId_configure["respondToConfigure → configureThread"]
  Browser -->|POST /api/threads/:threadId/continue| R_POST_api_threads_threadId_continue["respondToContinue → continueThread → ThreadRuntime.continueRun"]
  R_POST_api_threads_threadId_continue --> H_respondToContinue_continueThread_ThreadRuntime_continueRun["continueThread()"]
  H_respondToContinue_continueThread_ThreadRuntime_continueRun --> Runtime
  Browser -->|POST /api/threads/:threadId/dialog| R_POST_api_threads_threadId_dialog["respondToDialog → answerThreadDialog"]
  R_POST_api_threads_threadId_dialog --> H_respondToDialog_answerThreadDialog["answerThreadDialog()"]
  H_respondToDialog_answerThreadDialog --> Runtime
  Browser -->|POST /api/threads/:threadId/fork| R_POST_api_threads_threadId_fork["respondToFork → forkThreadAt → AgentHost.forkThread"]
  R_POST_api_threads_threadId_fork --> H_respondToFork_forkThreadAt_AgentHost_forkThread["forkThreadAt()"]
  H_respondToFork_forkThreadAt_AgentHost_forkThread --> Storage
  Browser -->|POST /api/threads/:threadId/mode| R_POST_api_threads_threadId_mode["respondToModeChange → changeThreadMode"]
  R_POST_api_threads_threadId_mode --> H_respondToModeChange_changeThreadMode["changeThreadMode()"]
  H_respondToModeChange_changeThreadMode --> Runtime
  Browser -->|POST /api/threads/:threadId/prompt| R_POST_api_threads_threadId_prompt["respondToPrompt → promptThread"]
  R_POST_api_threads_threadId_prompt --> H_respondToPrompt_promptThread["promptThread()"]
  H_respondToPrompt_promptThread --> Runtime
  Storage["AgentHost (storage)"]
  Runtime["ThreadRuntime"] --> Observations["EnsoObservation stream"]
Node Contract Input Source
respondToProjectRegistration → ProjectRegistry.register POST /api/projects body: EnsoProjectRegistration — an absolute path strictly under a root the ENSO_HOME config declares (projects.roots), optional title packages/web/src/server/index.ts:1337
respondToProjectRemoval → ProjectRegistry.remove DELETE /api/projects/:projectId path: projectId (sixteen hex digits) packages/web/src/server/index.ts:140
respondToPromptRemoval → PromptLibrary.remove DELETE /api/prompts/:id path: id; header: If-Match: <ETag> (the version read) or If-Match: * packages/web/src/server/index.ts:138
respondToPromptWrite → PromptLibrary.write PUT /api/prompts/:id path: id; headers: exactly one precondition — If-None-Match: * (create: no file of that name), If-Match: <ETag> (replace the version read), If-Match: * (replace whatever is there); body: EnsoLibraryPromptWrite — name, description, category (system, append or session) and the markdown body packages/web/src/server/index.ts:137
respondToAbort → abortThread POST /api/threads/:threadId/abort path: threadId; no body packages/web/src/server/index.ts:106
packages/web/src/server/follow.ts:895
respondToHostCommand → runHostCommand POST /api/threads/:threadId/command path: threadId; body: EnsoHostCommandRun — model with provider/id, thinking with a level, login with provider/authType, logout with a provider, compact with nothing; a view row (context, fork) is the page’s to do, never run packages/web/src/server/index.ts:110
respondToConfigure → configureThread POST /api/threads/:threadId/configure path: threadId; body: EnsoSessionConfiguration — optional systemPrompt (a library prompt id, system or append, or null for pi’s default; absent leaves the session’s as it is), model (provider/id), thinking (a level that model supports) and mode; applied in that order under one lease, a value equal to the current one left alone packages/web/src/server/index.ts:112
respondToContinue → continueThread → ThreadRuntime.continueRun POST /api/threads/:threadId/continue path: threadId; no body packages/web/src/server/index.ts:127
packages/web/src/server/follow.ts:440
respondToDialog → answerThreadDialog POST /api/threads/:threadId/dialog path: threadId; body: DialogAnswer packages/web/src/server/index.ts:105
packages/web/src/server/follow.ts:518
respondToFork → forkThreadAt → AgentHost.forkThread POST /api/threads/:threadId/fork path: threadId; body: EnsoForkRequest — entryId, the pi entry id of one of the thread’s user messages; the fork is pi’s /fork at position before, taken from the session file packages/web/src/server/index.ts:125
packages/web/src/server/follow.ts:851
respondToModeChange → changeThreadMode POST /api/threads/:threadId/mode path: threadId; body: EnsoPermissionModeChange packages/web/src/server/index.ts:108
packages/web/src/server/follow.ts:551
respondToPrompt → promptThread POST /api/threads/:threadId/prompt path: threadId; body: EnsoPromptBody packages/web/src/server/index.ts:104
packages/web/src/server/follow.ts:340

One pi event becomes one observation and crosses six stages to reach the browser store. The union declares 26 kinds; the mapper produces 24 of them, and the rest are produced by the prompt route (see Observations for the per-kind producer, carrier and consumer).

flowchart LR
  Pi["pi runtime event"]
  Pi --> Mapper["mapPiEvent (24 kinds)"]
  Mapper --> Schema["EnsoObservation"]
  Schema --> Frame["observation frame"]
  Frame --> Connection["follow connection"]
  Connection --> Adapter["to-agui chunk"]
  Adapter --> Router["custom event router"]
  Router --> Store["browser store"]
Stage Contract Source
mapPiEvent (24 kinds) Observations packages/web/src/host/map-events.ts:298
EnsoObservation Core exports packages/core/src/observation.ts:633
observation frame Observations packages/web/src/server/follow.ts:215
follow connection Routes packages/web/src/follow-connection.ts:493
to-agui chunk Observations packages/web/src/to-agui.ts:76
custom event router Observations packages/web/src/custom-event-router.ts:212

Kinds, in the union’s order of declaration: text-delta, thinking-delta, tool-call, tool-result, custom-entry, permission-mode, permission-mode-rejected, model, message-start, message-end, session-usage, branch-compacted, login, turn-start, turn-end, provider-refused, provider-retry, provider-retry-ended, queue, prompt-rejected, extension-error, extension-ui-request, dialog-settled, run-ended, settled, unmapped.