docs(ticket13): inventaire transport backend et frontend (B0)
Lot B0 du chantier server/client mode : cartographie des surfaces de transport existantes, préalable à l'extraction du cœur backend commun. - docs/ticket13-b0-backend-transport-inventory.md : inventaire backend. - docs/ticket13-f0-frontend-transport-inventory.md : inventaire frontend. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
708
docs/ticket13-b0-backend-transport-inventory.md
Normal file
708
docs/ticket13-b0-backend-transport-inventory.md
Normal file
@ -0,0 +1,708 @@
|
||||
# Ticket #13 B0 - Backend transport inventory
|
||||
|
||||
Date: 2026-07-15
|
||||
Branch: `feature/ticket13-server-client-mode`
|
||||
|
||||
Scope: read-only inventory of the current Tauri backend transport surface in
|
||||
`crates/app-tauri`. No production code change is part of B0.
|
||||
|
||||
Architect ticket #13 target: keep desktop Tauri and add server/client mode on a
|
||||
shared backend core. Future web transport is HTTP request/response plus a
|
||||
dedicated WebSocket for PTY/live streams.
|
||||
|
||||
## Source anchors
|
||||
|
||||
- Tauri command registration: `crates/app-tauri/src/lib.rs:153`
|
||||
- Composition root and `AppState`: `crates/app-tauri/src/state.rs:738`,
|
||||
`crates/app-tauri/src/state.rs:1058`
|
||||
- PTY Tauri Channel bridge: `crates/app-tauri/src/pty.rs:1`
|
||||
- Structured chat Tauri Channel bridge: `crates/app-tauri/src/chat.rs:1`
|
||||
- Domain event relay: `crates/app-tauri/src/events.rs:23`,
|
||||
`crates/app-tauri/src/events.rs:1107`
|
||||
- Desktop window commands/events: `crates/app-tauri/src/commands.rs:2325`
|
||||
- Shutdown/window lifecycle wiring: `crates/app-tauri/src/lib.rs:80`
|
||||
|
||||
## Classification rules
|
||||
|
||||
- `request/response`: one Tauri `invoke` maps to one backend result. HTTP
|
||||
candidate.
|
||||
- `stream`: command uses `tauri::ipc::Channel` or controls a live stream/session.
|
||||
WebSocket candidate, sometimes with a small HTTP control equivalent.
|
||||
- `event`: backend push currently emitted through Tauri global events. WebSocket
|
||||
live/event frame candidate unless desktop-specific.
|
||||
- `desktop-only`: OS windows, Tauri path/lifecycle/dialog composition. Not part
|
||||
of the web client transport.
|
||||
|
||||
## Exposed Tauri commands
|
||||
|
||||
### Core/projects/permissions
|
||||
|
||||
| Command | Class | Current boundary |
|
||||
| --- | --- | --- |
|
||||
| `health` | request/response | `State<AppState>.health` |
|
||||
| `create_project` | request/response | `create_project` use case |
|
||||
| `open_project` | request/response | `open_project`, plus open-time reconciliation/wiring |
|
||||
| `close_project` | request/response | `close_project` |
|
||||
| `list_projects` | request/response | `list_projects` |
|
||||
| `read_project_context` | request/response | project FS read use case |
|
||||
| `update_project_context` | request/response | project FS write use case |
|
||||
| `get_project_permissions` | request/response | permission read use case |
|
||||
| `update_project_permissions` | request/response | permission write use case |
|
||||
| `update_agent_permissions` | request/response | permission override write use case |
|
||||
| `resolve_agent_permissions` | request/response | effective permission read use case |
|
||||
|
||||
### PTY terminals
|
||||
|
||||
| Command | Class | Current boundary |
|
||||
| --- | --- | --- |
|
||||
| `open_terminal` | stream | Spawns PTY, registers `Channel<PtyChunk>`, starts output pump |
|
||||
| `write_terminal` | stream/control | Writes bytes to live PTY |
|
||||
| `resize_terminal` | stream/control | Resizes live PTY |
|
||||
| `close_terminal` | stream/control | Kills PTY and unregisters channel |
|
||||
| `reattach_terminal` | stream | Reads scrollback, replaces `Channel<PtyChunk>`, starts new pump |
|
||||
|
||||
Notes:
|
||||
|
||||
- `PtyChunk = Vec<u8>` today (`crates/app-tauri/src/pty.rs:29`).
|
||||
- The transport-specific registry is `PtyBridge`, stored in `AppState`.
|
||||
- High-frequency PTY bytes are explicitly excluded from global Tauri events.
|
||||
|
||||
### Layouts
|
||||
|
||||
| Command | Class | Current boundary |
|
||||
| --- | --- | --- |
|
||||
| `load_layout` | request/response | layout read use case |
|
||||
| `mutate_layout` | request/response | layout mutation use case, emits `LayoutChanged` |
|
||||
| `list_layouts` | request/response | named layout list |
|
||||
| `create_layout` | request/response | named layout create |
|
||||
| `rename_layout` | request/response | named layout rename |
|
||||
| `delete_layout` | request/response | named layout delete |
|
||||
| `set_active_layout` | request/response | active layout write |
|
||||
|
||||
### First-run, profiles, local models, embedders
|
||||
|
||||
| Command | Class | Current boundary |
|
||||
| --- | --- | --- |
|
||||
| `first_run_state` | request/response | first-run read use case |
|
||||
| `reference_profiles` | request/response | reference profile catalog |
|
||||
| `detect_profiles` | request/response | runtime detection use case |
|
||||
| `list_profiles` | request/response | profile store read |
|
||||
| `save_profile` | request/response | profile upsert |
|
||||
| `clone_opencode_profile_from_seed` | request/response | profile clone |
|
||||
| `delete_profile` | request/response | profile delete |
|
||||
| `configure_profiles` | request/response | first-run save |
|
||||
| `list_model_servers` | request/response | local model server config read |
|
||||
| `save_model_server` | request/response | local model server config write |
|
||||
| `preview_model_server_command` | request/response | pure command preview |
|
||||
| `delete_model_server` | request/response | local model server config delete |
|
||||
| `list_embedder_profiles` | request/response | embedder profile read |
|
||||
| `save_embedder_profile` | request/response | embedder profile write |
|
||||
| `delete_embedder_profile` | request/response | embedder profile delete |
|
||||
| `describe_embedder_engines` | request/response | embedder engine capabilities |
|
||||
| `dismiss_embedder_suggestion` | request/response | suggestion state write |
|
||||
|
||||
### Agents, live state, chat and resume
|
||||
|
||||
| Command | Class | Current boundary |
|
||||
| --- | --- | --- |
|
||||
| `create_agent` | request/response | agent manifest/context creation |
|
||||
| `list_agents` | request/response | manifest read |
|
||||
| `get_project_work_state` | request/response | read-model aggregation |
|
||||
| `read_conversation_page` | request/response | paginated transcript read |
|
||||
| `list_live_agents` | request/response | in-memory live registry read |
|
||||
| `attach_live_agent` | request/response | live session rebind use case |
|
||||
| `stop_live_agent` | request/response | live session stop use case |
|
||||
| `read_agent_context` | request/response | agent Markdown read |
|
||||
| `update_agent_context` | request/response | agent Markdown write |
|
||||
| `delete_agent` | request/response | manifest/context delete |
|
||||
| `inspect_conversation` | request/response | best-effort conversation inspection |
|
||||
| `launch_agent` | stream | Spawns/reuses PTY or structured session, may register `Channel<PtyChunk>` |
|
||||
| `change_agent_profile` | request/response | hot-swap use case; may affect live session |
|
||||
| `agent_send` | stream | Structured chat turn, registers `Channel<ReplyChunk>`, pumps reply stream |
|
||||
| `cancel_resume` | request/response | cancels scheduled auto-resume |
|
||||
| `set_resume_at` | request/response | human-set resume time |
|
||||
| `interrupt_agent` | request/response | orchestrator interrupt |
|
||||
| `delegation_delivered` | request/response | frontend write-portal ack |
|
||||
| `set_front_attached` | request/response | frontend mounted/unmounted signal |
|
||||
| `reattach_agent_chat` | stream | Replaces `Channel<ReplyChunk>`, returns chat scrollback |
|
||||
| `close_agent_session` | request/response | closes live agent/structured state |
|
||||
| `list_resumable_agents` | request/response | resumable agent inventory |
|
||||
|
||||
Structured chat stream:
|
||||
|
||||
- `ReplyChunk` is tagged by `kind`: `textDelta`, `toolActivity`, `final`,
|
||||
`error` (`crates/app-tauri/src/dto.rs:1760`).
|
||||
- `ChatBridge` retains bounded scrollback and the current optional channel.
|
||||
- `agent_send` is a turn command plus streaming response; in HTTP/WS terms it
|
||||
should become a WS client frame that starts a turn on an attached session.
|
||||
|
||||
### Templates
|
||||
|
||||
| Command | Class | Current boundary |
|
||||
| --- | --- | --- |
|
||||
| `create_template` | request/response | template create |
|
||||
| `update_template` | request/response | template update, emits drift/update events |
|
||||
| `list_templates` | request/response | template list |
|
||||
| `delete_template` | request/response | template delete |
|
||||
| `create_agent_from_template` | request/response | agent creation from template |
|
||||
| `detect_agent_drift` | request/response | drift inventory |
|
||||
| `sync_agent_with_template` | request/response | sync write |
|
||||
|
||||
### Git
|
||||
|
||||
| Command | Class | Current boundary |
|
||||
| --- | --- | --- |
|
||||
| `git_status` | request/response | git status use case |
|
||||
| `git_stage` | request/response | git stage use case |
|
||||
| `git_unstage` | request/response | git unstage use case |
|
||||
| `git_commit` | request/response | git commit use case |
|
||||
| `git_branches` | request/response | branch list |
|
||||
| `git_checkout` | request/response | branch checkout |
|
||||
| `git_log` | request/response | log read |
|
||||
| `git_init` | request/response | repo init |
|
||||
| `git_graph` | request/response | graph read |
|
||||
|
||||
### Tickets and sprints
|
||||
|
||||
| Command | Class | Current boundary |
|
||||
| --- | --- | --- |
|
||||
| `ticket_create` | request/response | ticket create |
|
||||
| `ticket_read` | request/response | ticket read |
|
||||
| `ticket_delete` | request/response | ticket delete |
|
||||
| `open_ticket_chat` | request/response | ephemeral ticket assistant open |
|
||||
| `close_ticket_chat` | request/response | ephemeral ticket assistant close |
|
||||
| `ticket_list` | request/response | ticket list |
|
||||
| `ticket_update` | request/response | ticket update |
|
||||
| `ticket_read_carnet` | request/response | carnet read |
|
||||
| `ticket_update_carnet` | request/response | carnet write |
|
||||
| `ticket_link` | request/response | link tickets |
|
||||
| `ticket_unlink` | request/response | unlink tickets |
|
||||
| `ticket_assign` | request/response | assign/unassign ticket agent |
|
||||
| `sprint_create` | request/response | sprint create |
|
||||
| `sprint_list` | request/response | sprint list |
|
||||
| `sprint_rename` | request/response | sprint rename |
|
||||
| `sprint_reorder` | request/response | sprint reorder |
|
||||
| `sprint_delete` | request/response | sprint delete |
|
||||
| `ticket_assign_sprint` | request/response | ticket sprint assignment |
|
||||
| `ticket_unassign_sprint` | request/response | ticket sprint removal |
|
||||
|
||||
### Skills and memory
|
||||
|
||||
| Command | Class | Current boundary |
|
||||
| --- | --- | --- |
|
||||
| `create_skill` | request/response | skill create |
|
||||
| `update_skill` | request/response | skill update |
|
||||
| `list_skills` | request/response | skill list |
|
||||
| `delete_skill` | request/response | skill delete |
|
||||
| `assign_skill_to_agent` | request/response | skill assignment |
|
||||
| `unassign_skill_from_agent` | request/response | skill unassignment |
|
||||
| `create_memory` | request/response | memory create |
|
||||
| `update_memory` | request/response | memory update |
|
||||
| `list_memories` | request/response | memory list |
|
||||
| `get_memory` | request/response | memory read |
|
||||
| `delete_memory` | request/response | memory delete |
|
||||
| `read_memory_index` | request/response | memory index read |
|
||||
| `recall_memory` | request/response | memory recall |
|
||||
| `resolve_memory_links` | request/response | memory link resolution |
|
||||
|
||||
### Background tasks
|
||||
|
||||
| Command | Class | Current boundary |
|
||||
| --- | --- | --- |
|
||||
| `spawn_background_command` | request/response | creates command-backed background task |
|
||||
| `cancel_background_task` | request/response | cancels task |
|
||||
| `retry_background_task` | request/response | retries task |
|
||||
| `list_background_tasks` | request/response | task read-model list |
|
||||
|
||||
Lifecycle is pushed through `BackgroundTaskChanged` domain events; initial list is
|
||||
request/response.
|
||||
|
||||
### Desktop windows and focus
|
||||
|
||||
| Command | Class | Current boundary |
|
||||
| --- | --- | --- |
|
||||
| `set_focused_project` | desktop-only + event | Tauri window/panel focus state; emits `focused-project://changed` |
|
||||
| `get_focused_project` | desktop-only | Tauri panel state read |
|
||||
| `list_open_view_windows` | desktop-only | `AppHandle.webview_windows()` |
|
||||
| `open_view_window` | desktop-only | `WebviewWindowBuilder` |
|
||||
| `close_view_window` | desktop-only | closes Tauri OS window |
|
||||
| `move_tab_to_new_window` | desktop-only | use case plus `WebviewWindowBuilder` |
|
||||
|
||||
These do not migrate as-is to web. The shared backend may still keep workspace
|
||||
topology use cases, but the OS-window adapter remains desktop-only.
|
||||
|
||||
## Tauri Channel usage
|
||||
|
||||
| Location | Channel type | Producer | Consumer | Web mapping |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `open_terminal` | `Channel<PtyChunk>` | PTY output pump | xterm view | WS `output` frames after `open_terminal` |
|
||||
| `reattach_terminal` | `Channel<PtyChunk>` | PTY output pump | xterm view | WS `attached` + `output` frames after `attach_terminal` |
|
||||
| `launch_agent` | `Channel<PtyChunk>` | PTY output pump for raw CLI agents | xterm/chat terminal view | WS `output` frames; structured sessions do not use this channel |
|
||||
| `agent_send` | `Channel<ReplyChunk>` | structured reply pump | chat view | WS `chat.output` or generic live frames |
|
||||
| `reattach_agent_chat` | `Channel<ReplyChunk>` | structured reply pump | chat view | WS attached/replay for structured session |
|
||||
|
||||
Transport-owned registries:
|
||||
|
||||
- `PtyBridge`: session id -> `(generation, Channel<PtyChunk>)`.
|
||||
- `ChatBridge`: session id -> `{generation, Option<Channel<ReplyChunk>>, scrollback}`.
|
||||
|
||||
These should not descend into domain/application. Their backend-core equivalent
|
||||
should be a transport-neutral outbound sink/subscription registry.
|
||||
|
||||
## Events emitted today
|
||||
|
||||
### Global domain event relay
|
||||
|
||||
`events::spawn_relay` subscribes to `TokioBroadcastEventBus` and emits:
|
||||
|
||||
- Tauri event name `domain://event`
|
||||
- Payload `DomainEventDto`, tagged by `type`
|
||||
- Skips `DomainEvent::PtyOutput`; PTY bytes use per-session channels
|
||||
|
||||
Current `DomainEventDto` variants:
|
||||
|
||||
- Project/lifecycle: `projectCreated`, `layoutChanged`, `remoteConnected`
|
||||
- Agents: `agentLaunched`, `agentLaunchFailed`, `agentExited`,
|
||||
`agentBusyChanged`, `agentProfileChanged`, `agentLivenessChanged`,
|
||||
`agentRateLimited`, `agentResumeScheduled`, `agentResumeCancelled`,
|
||||
`agentResumed`, `agentRateLimitSuspected`
|
||||
- Delegation/orchestration: `delegationReady`, `agentReplied`,
|
||||
`agentAnnouncement`, `orchestratorRequestProcessed`, `orchestratorChanged`
|
||||
- Background work: `backgroundTaskChanged`, `agentInboxChanged`,
|
||||
`agentWakeChanged`
|
||||
- Templates/skills: `templateUpdated`, `agentDriftDetected`, `agentSynced`,
|
||||
`skillAssigned`
|
||||
- Git: `gitStateChanged`
|
||||
- Tickets/sprints: `issueCreated`, `issueUpdated`, `issueDeleted`,
|
||||
`issueStatusChanged`, `issuePriorityChanged`, `issueCarnetUpdated`,
|
||||
`issueLinked`, `issueUnlinked`, `issueAgentAssigned`,
|
||||
`issueAgentUnassigned`, `sprintCreated`, `sprintRenamed`,
|
||||
`sprintReordered`, `sprintDeleted`, `issueSprintChanged`
|
||||
- Memory/embedder: `memorySaved`, `memoryDeleted`, `memoryIndexRebuilt`,
|
||||
`embedderSuggested`
|
||||
- Model server: `modelServerStatusChanged`
|
||||
- PTY placeholder: `ptyOutput` exists in DTO but is not emitted by the global
|
||||
relay
|
||||
|
||||
### Dedicated global events
|
||||
|
||||
- `model_server_status_changed`: emitted in addition to `domain://event` for
|
||||
`ModelServerStatusChanged`.
|
||||
- `agent_launch_failed`: emitted in addition to `domain://event` for
|
||||
`AgentLaunchFailed`.
|
||||
- `view-window://lifecycle`: desktop-only, payload `{kind, panel, label}`.
|
||||
- `focused-project://changed`: desktop-only, payload `{project}`.
|
||||
|
||||
For web, the domain-event set maps naturally to a low-frequency live WS stream.
|
||||
The duplicate dedicated events should be normalized in the shared core contract
|
||||
or kept as compatibility aliases only in the Tauri adapter.
|
||||
|
||||
## `tauri::State<AppState>` composition root inventory
|
||||
|
||||
`AppState::build(app_data_dir)` is the current composition root. It constructs
|
||||
concrete adapters and use cases, then Tauri stores it through `app.manage`.
|
||||
|
||||
### Concrete adapters and infra state currently built there
|
||||
|
||||
- `TokioBroadcastEventBus`
|
||||
- `SystemClock`
|
||||
- `UuidGenerator`
|
||||
- `LocalFileSystem`
|
||||
- `FsProjectStore`
|
||||
- `FsWindowStateStore`
|
||||
- `PortablePtyAdapter` with sandbox enforcer
|
||||
- `TerminalSessions`
|
||||
- `StructuredSessions`
|
||||
- `StructuredSessionFactory`
|
||||
- `LocalProcessSpawner`
|
||||
- `CliAgentRuntime`
|
||||
- `FsProfileStore`
|
||||
- local model server manager/use cases
|
||||
- issue/ticket stores and providers
|
||||
- `PtyBridge` and `ChatBridge` transport bridges
|
||||
- profile/template/git/skill/memory/embedder stores and use cases
|
||||
- orchestrator service, watchers and per-project MCP servers
|
||||
- session-limit service, resume contexts and turn watcher
|
||||
- background task runner/store
|
||||
- focused project mutex
|
||||
- home/app-data derived paths
|
||||
|
||||
### Boundary that must be extracted
|
||||
|
||||
Current shape:
|
||||
|
||||
```text
|
||||
React -> @tauri invoke/listen/Channel
|
||||
-> app-tauri commands/events/bridges
|
||||
-> AppState concrete composition root
|
||||
-> application use cases + infrastructure adapters
|
||||
```
|
||||
|
||||
Target shape:
|
||||
|
||||
```text
|
||||
React -> TS gateways
|
||||
-> Tauri adapter OR HTTP+WS adapter
|
||||
-> driving adapter calls shared backend core facade
|
||||
-> application use cases + infrastructure adapters
|
||||
```
|
||||
|
||||
The extraction line is inside `app-tauri::state`:
|
||||
|
||||
- Move reusable construction into a shared backend core crate/facade, e.g.
|
||||
`BackendCore::build(config)`.
|
||||
- Keep Tauri-only concerns in `app-tauri`: resolving `app_data_dir` via Tauri
|
||||
path API, `app.manage`, command registration, `AppHandle`/`WebviewWindow`,
|
||||
shutdown hooks, Tauri event emission, Tauri `Channel` bridges, dialog plugin.
|
||||
- The future web server adapter should own HTTP route registration, TLS/pairing
|
||||
integration, WS connection lifecycle, CORS/origin checks and WS sink mapping.
|
||||
- The core should expose transport-neutral methods for the request/response
|
||||
use cases and transport-neutral subscription/sink APIs for PTY/chat/live
|
||||
output.
|
||||
|
||||
Do not move `PtyBridge` or `ChatBridge` as-is. They are Tauri transport
|
||||
adapters. Move the underlying concept: attach/detach a sink to a live
|
||||
session, replay bounded scrollback, and deliver ordered output.
|
||||
|
||||
## HTTP DTO first draft
|
||||
|
||||
Naming below keeps current DTO semantics and camelCase fields. Exact paths are a
|
||||
proposal for B1/B3; they can be namespaced by project where useful.
|
||||
|
||||
### Request/response examples
|
||||
|
||||
```http
|
||||
GET /api/health
|
||||
POST /api/projects
|
||||
GET /api/projects
|
||||
POST /api/projects/{projectId}/open
|
||||
POST /api/projects/{projectId}/close
|
||||
GET /api/projects/{projectId}/context
|
||||
PUT /api/projects/{projectId}/context
|
||||
GET /api/projects/{projectId}/layout
|
||||
POST /api/projects/{projectId}/layout/mutate
|
||||
GET /api/projects/{projectId}/agents
|
||||
POST /api/projects/{projectId}/agents
|
||||
GET /api/projects/{projectId}/agents/{agentId}/context
|
||||
PUT /api/projects/{projectId}/agents/{agentId}/context
|
||||
DELETE /api/projects/{projectId}/agents/{agentId}
|
||||
GET /api/projects/{projectId}/work-state
|
||||
GET /api/projects/{projectId}/conversation-page
|
||||
GET /api/projects/{projectId}/git/status
|
||||
POST /api/projects/{projectId}/git/stage
|
||||
POST /api/projects/{projectId}/git/commit
|
||||
GET /api/tickets
|
||||
POST /api/tickets
|
||||
GET /api/tickets/{ref}
|
||||
PATCH /api/tickets/{ref}
|
||||
GET /api/templates
|
||||
POST /api/templates
|
||||
GET /api/background-tasks
|
||||
POST /api/background-tasks
|
||||
```
|
||||
|
||||
Errors should preserve `ErrorDto`:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "NOT_FOUND",
|
||||
"message": "project ..."
|
||||
}
|
||||
```
|
||||
|
||||
## WebSocket first draft
|
||||
|
||||
One dedicated WS endpoint is enough for B5 V1:
|
||||
|
||||
```text
|
||||
GET /ws/live?token=... (token normally via Authorization/cookie, not URL in prod)
|
||||
```
|
||||
|
||||
Authentication note from ticket #13: pairing/token must travel only over HTTPS
|
||||
and not as a URL secret. The query above is only a sketch; production should use
|
||||
`Authorization: Bearer` during WS upgrade or an HTTPS-only secure cookie.
|
||||
|
||||
### Common envelope
|
||||
|
||||
Client to server:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "client-msg-uuid",
|
||||
"kind": "terminal.input",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
|
||||
Server to client:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "server-msg-uuid",
|
||||
"replyTo": "client-msg-uuid",
|
||||
"kind": "terminal.status",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
|
||||
`replyTo` is present for command acknowledgements, absent for unsolicited live
|
||||
events/output.
|
||||
|
||||
### PTY client -> server frames
|
||||
|
||||
`attach_terminal`
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "1",
|
||||
"kind": "terminal.attach",
|
||||
"payload": {
|
||||
"sessionId": "uuid",
|
||||
"lastSeq": 42
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`open_terminal`
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "2",
|
||||
"kind": "terminal.open",
|
||||
"payload": {
|
||||
"projectId": "uuid",
|
||||
"nodeId": "uuid-or-null",
|
||||
"cwd": "/abs/path",
|
||||
"rows": 30,
|
||||
"cols": 120
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`launch_agent`
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "3",
|
||||
"kind": "agent.launch",
|
||||
"payload": {
|
||||
"projectId": "uuid",
|
||||
"agentId": "uuid",
|
||||
"nodeId": "uuid-or-null",
|
||||
"rows": 30,
|
||||
"cols": 120,
|
||||
"conversationId": "engine-conversation-id-or-null"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`input`
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "4",
|
||||
"kind": "terminal.input",
|
||||
"payload": {
|
||||
"sessionId": "uuid",
|
||||
"bytesBase64": "DQ=="
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`resize`
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "5",
|
||||
"kind": "terminal.resize",
|
||||
"payload": {
|
||||
"sessionId": "uuid",
|
||||
"rows": 40,
|
||||
"cols": 140
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`detach`
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "6",
|
||||
"kind": "terminal.detach",
|
||||
"payload": {
|
||||
"sessionId": "uuid"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`close`
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "7",
|
||||
"kind": "terminal.close",
|
||||
"payload": {
|
||||
"sessionId": "uuid"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`ping`
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "8",
|
||||
"kind": "ping",
|
||||
"payload": {
|
||||
"atMs": 1784123456789
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### PTY server -> client frames
|
||||
|
||||
`attached`
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "terminal.attached",
|
||||
"replyTo": "1",
|
||||
"payload": {
|
||||
"session": { "sessionId": "uuid", "projectId": "uuid", "nodeId": "uuid", "rows": 30, "cols": 120 },
|
||||
"nextSeq": 43,
|
||||
"scrollback": [
|
||||
{ "seq": 1, "bytesBase64": "..." }
|
||||
],
|
||||
"gap": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`output`
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "terminal.output",
|
||||
"payload": {
|
||||
"sessionId": "uuid",
|
||||
"seq": 43,
|
||||
"bytesBase64": "..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`status`
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "terminal.status",
|
||||
"payload": {
|
||||
"sessionId": "uuid",
|
||||
"status": "running"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Suggested status values: `starting`, `running`, `exited`, `closed`,
|
||||
`detached`, `reattached`.
|
||||
|
||||
`error`
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "error",
|
||||
"replyTo": "7",
|
||||
"payload": {
|
||||
"code": "NOT_FOUND",
|
||||
"message": "terminal session ..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`pong`
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "pong",
|
||||
"replyTo": "8",
|
||||
"payload": {
|
||||
"atMs": 1784123456789
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Structured chat frames
|
||||
|
||||
The ticket #13 PTY contract does not require structured chat in B5, but the
|
||||
current backend already has a second stream. To avoid a later incompatibility,
|
||||
reserve a parallel namespace:
|
||||
|
||||
Client:
|
||||
|
||||
```json
|
||||
{ "id": "9", "kind": "chat.attach", "payload": { "sessionId": "uuid" } }
|
||||
{ "id": "10", "kind": "chat.send", "payload": { "sessionId": "uuid", "prompt": "..." } }
|
||||
{ "id": "11", "kind": "chat.detach", "payload": { "sessionId": "uuid" } }
|
||||
```
|
||||
|
||||
Server:
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "chat.attached",
|
||||
"replyTo": "9",
|
||||
"payload": {
|
||||
"sessionId": "uuid",
|
||||
"scrollback": [
|
||||
{ "kind": "textDelta", "text": "..." }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "chat.output",
|
||||
"payload": {
|
||||
"sessionId": "uuid",
|
||||
"chunk": { "kind": "final", "content": "..." }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Low-frequency live events
|
||||
|
||||
Domain events can be pushed on the same WS connection:
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "event.domain",
|
||||
"payload": {
|
||||
"type": "agentBusyChanged",
|
||||
"agentId": "uuid",
|
||||
"busy": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This replaces Tauri `listen("domain://event")` for web. Desktop can keep the
|
||||
existing event relay.
|
||||
|
||||
## Plan impact / risks found
|
||||
|
||||
No finding invalidates the ticket #13 lot plan.
|
||||
|
||||
Points to decide early in B1/B2:
|
||||
|
||||
- The duplicate Tauri registration of `detect_agent_drift` in
|
||||
`generate_handler!` is harmless for B0 but should be cleaned in a separate fix.
|
||||
- `open_project` currently also triggers open-time reconciliation and per-project
|
||||
runtime side effects. The shared backend core facade should make those explicit
|
||||
methods, not hide them in transport commands.
|
||||
- `launch_agent` has both raw PTY and structured-session branches. B5 PTY V1 can
|
||||
implement raw PTY first, but the WS namespace should reserve chat/live frames.
|
||||
- `PtyBridge` and `ChatBridge` contain transport scrollback/replay behavior.
|
||||
Extract the behavior as a core subscription contract; do not leak Tauri
|
||||
`Channel` into the future core.
|
||||
- Desktop window/focus commands are cleanly separable as desktop-only. Workspace
|
||||
topology can remain shared, OS-window creation cannot.
|
||||
Reference in New Issue
Block a user