the officer shell: designs per user, and the handoff desk
manifest, schema, router and two panels. the editor is hosted and saving; the last step is not finished and the readme says so. designs are rows scoped per user, with userId in every WHERE rather than checked after the read — another person's design is 'not found', so the id space cannot be probed. the Doc is stored whole as jsonb because upstream adds fields to it constantly and none of them should be a migration here. the handoff is the reason the plugin exists and it is the part that does not work: /api/chat has NO session-creation route, a session is constructed by the browser opening a websocket, so a plugin has nothing to POST to. onExport returns false and the clipboard still runs, which is upstream's own behaviour — degraded, not broken. docs/handoff.md names the one platform change that would finish it, and argues it should be generic rather than ours. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
# The handoff, and the one platform hook it needs
|
||||
|
||||
This plugin exists for a single step: **the generated prompt should not reach a clipboard.** It should
|
||||
open a chat session with the agent, already containing the prompt, pointed at a directory on this
|
||||
machine.
|
||||
|
||||
That step is not finished, and the reason is worth writing down precisely, because it is a platform
|
||||
gap rather than an unwritten function.
|
||||
|
||||
---
|
||||
|
||||
## Why a plugin cannot do this today
|
||||
|
||||
**`/api/chat` has no session-creation route.** Reading `src/servers/api/chat/chat.ts`, the surface is
|
||||
`GET /sessions`, `GET /sessions/:id`, `DELETE /sessions/:id`, the task verbs and the usage and model
|
||||
readers. There is no `POST /sessions`.
|
||||
|
||||
A session is created by the **browser** opening `/api/chat/ws` and sending its first message. The
|
||||
socket is the constructor. So there is nothing a plugin's server can POST to in order to start one,
|
||||
and nothing it can hand a prompt to.
|
||||
|
||||
That is a reasonable design — it keeps session lifetime tied to a live connection — but it means
|
||||
"start a session containing this text" is not expressible from a plugin.
|
||||
|
||||
## What is built
|
||||
|
||||
`POST /api/m3e-canvas/_handoff` takes `{ prompt, title }`, holds it in memory against a one-time id
|
||||
with a ten-minute expiry, and answers:
|
||||
|
||||
```json
|
||||
{ "id": "…", "chatUrl": "/chat/new?handoff=…", "ready": false }
|
||||
```
|
||||
|
||||
`GET /api/m3e-canvas/_handoff/:id` collects it once and forgets it. A handoff belongs to whoever made
|
||||
it; another user asking is told it does not exist rather than that it is theirs.
|
||||
|
||||
`ready: false` is the honest part of the response. **Nothing reads `?handoff=` yet.**
|
||||
|
||||
Meanwhile `onExport` returns `false`, so the editor still writes the clipboard. The feature is
|
||||
degraded rather than absent: you get the prompt, you just paste it yourself — which is exactly what
|
||||
upstream does, so nothing is worse than before.
|
||||
|
||||
## What would finish it
|
||||
|
||||
One small change, in the platform rather than here. `/chat/new` reads a `handoff` search parameter, and
|
||||
when present fetches `/api/m3e-canvas/_handoff/:id` and puts the prompt in the composer before the
|
||||
first send.
|
||||
|
||||
That is a few lines in `SessionListPage`, and it is deliberately **not** written here for two reasons.
|
||||
It is a platform change, so it belongs to whoever owns the chat screen rather than arriving as a side
|
||||
effect of installing a drawing tool. And a plugin reaching into another surface's composer is the kind
|
||||
of coupling that is easy to add and unpleasant to find later.
|
||||
|
||||
### The shape worth arguing about first
|
||||
|
||||
If this is worth doing at all, it is probably worth doing **generically** — `?handoff=<pluginPrefix>/<id>`,
|
||||
resolved against the plugin that minted it, so the next plugin that wants to hand work to the agent
|
||||
does not add a second parameter. `todo/sendoff/` is already circling the same idea from another
|
||||
direction.
|
||||
|
||||
Until that is decided, the seam here is small enough to throw away and specific enough to be honest
|
||||
about what it does not do.
|
||||
|
||||
## The other half, unstarted
|
||||
|
||||
Even with the composer prefilled, "the agent writes the app" needs somewhere to write it. A design has
|
||||
no notion of a project directory, and picking one is a product decision:
|
||||
|
||||
- a directory the user names when handing off
|
||||
- a per-design directory remembered on the row
|
||||
- whatever the chat session's own working directory already is
|
||||
|
||||
The third is the least work and probably the right default, since a session started from a dashboard
|
||||
already has a directory. It is not implemented, and the row has no column for it.
|
||||
Reference in New Issue
Block a user