diff --git a/proto/agynio/api/agents/v1/agents.proto b/proto/agynio/api/agents/v1/agents.proto index b0d414e..3b7082c 100644 --- a/proto/agynio/api/agents/v1/agents.proto +++ b/proto/agynio/api/agents/v1/agents.proto @@ -45,6 +45,11 @@ service AgentsService { rpc UpdateSandboxRuntimeState(UpdateSandboxRuntimeStateRequest) returns (UpdateSandboxRuntimeStateResponse); rpc UpdateSandboxLastSession(UpdateSandboxLastSessionRequest) returns (UpdateSandboxLastSessionResponse); + // --- Sandbox Layouts --- + rpc GetSandboxLayout(GetSandboxLayoutRequest) returns (GetSandboxLayoutResponse); + rpc SetSandboxLayout(SetSandboxLayoutRequest) returns (SetSandboxLayoutResponse); + rpc SetSandboxLayoutDirectories(SetSandboxLayoutDirectoriesRequest) returns (SetSandboxLayoutDirectoriesResponse); + // --- Agent Instances --- rpc CreateInstance(CreateInstanceRequest) returns (CreateInstanceResponse); rpc GetInstance(GetInstanceRequest) returns (GetInstanceResponse); @@ -838,6 +843,81 @@ message UpdateSandboxLastSessionResponse { Sandbox sandbox = 1; } +// =========================================================================== +// Sandbox Layout +// =========================================================================== + +// One identity's ordered set of shells in one sandbox. +message SandboxLayout { + string sandbox_id = 1; + + // Whose layout this is. Resolved from authenticated context on the external + // methods; not accepted as request input. + string identity_id = 2; + + // Incremented on every write. + int64 version = 3; + + // Ordered. Position is display order. + repeated SandboxTab tabs = 4; +} + +message SandboxTab { + // Opaque, client-generated, ^[A-Za-z0-9_-]{1,64}$. Matches shell_id on a + // SHELL_ATTACH terminal session. + string shell_id = 1; + + // Assigned when the tab is opened; not reused within a layout. + int32 number = 2; + + // A caller-supplied name. Unset means the client derives one. + optional string name_override = 3; + + // Working directory, absolute. + optional string cwd = 4; + + // When a session last attached to this shell. + optional google.protobuf.Timestamp last_attached_at = 5; +} + +message GetSandboxLayoutRequest { + string sandbox_id = 1; +} + +message GetSandboxLayoutResponse { + // Empty at version 0 when no layout exists. + SandboxLayout layout = 1; +} + +message SetSandboxLayoutRequest { + string sandbox_id = 1; + + // The version the caller read. A mismatch returns FailedPrecondition. + int64 version = 2; + + repeated SandboxTab tabs = 3; +} + +message SetSandboxLayoutResponse { + SandboxLayout layout = 1; +} + +// Internal only. Sets cwd on tabs of every layout of one sandbox, matched by +// shell_id. Unmatched ids are ignored. Not version-checked. +message SetSandboxLayoutDirectoriesRequest { + string sandbox_id = 1; + repeated ShellDirectory directories = 2; +} + +message ShellDirectory { + string shell_id = 1; + string cwd = 2; +} + +message SetSandboxLayoutDirectoriesResponse { + int32 tabs_updated = 1; +} + // =========================================================================== // Volume // =========================================================================== diff --git a/proto/agynio/api/gateway/v1/agents.proto b/proto/agynio/api/gateway/v1/agents.proto index adae111..564d00b 100644 --- a/proto/agynio/api/gateway/v1/agents.proto +++ b/proto/agynio/api/gateway/v1/agents.proto @@ -36,6 +36,12 @@ service AgentsGateway { rpc DeleteSandbox(agynio.api.agents.v1.DeleteSandboxRequest) returns (agynio.api.agents.v1.DeleteSandboxResponse); rpc EnsureSandboxRunning(agynio.api.agents.v1.EnsureSandboxRunningRequest) returns (agynio.api.agents.v1.EnsureSandboxRunningResponse); + // --- Sandbox Layouts --- + // Operate on the caller's own layout; identity comes from authenticated + // context and is not a request field. + rpc GetSandboxLayout(agynio.api.agents.v1.GetSandboxLayoutRequest) returns (agynio.api.agents.v1.GetSandboxLayoutResponse); + rpc SetSandboxLayout(agynio.api.agents.v1.SetSandboxLayoutRequest) returns (agynio.api.agents.v1.SetSandboxLayoutResponse); + // --- Agent Instances --- rpc CreateInstance(agynio.api.agents.v1.CreateInstanceRequest) returns (agynio.api.agents.v1.CreateInstanceResponse); rpc GetInstance(agynio.api.agents.v1.GetInstanceRequest) returns (agynio.api.agents.v1.GetInstanceResponse); diff --git a/proto/agynio/api/gateway/v1/terminal.proto b/proto/agynio/api/gateway/v1/terminal.proto index f35127a..9da0088 100644 --- a/proto/agynio/api/gateway/v1/terminal.proto +++ b/proto/agynio/api/gateway/v1/terminal.proto @@ -24,6 +24,14 @@ message CreateTerminalSessionRequest { // The directory to serve. Meaningful only when kind is SYNC. Must be // absolute. string sync_root = 5; + + // Names the shell. Required when kind is SHELL_ATTACH, ignored otherwise. + // Opaque, ^[A-Za-z0-9_-]{1,64}$. + string shell_id = 6; + + // Working directory for the shell. Optional; applied when the shell is + // created. Must be absolute when set. + string shell_cwd = 7; } message CreateTerminalSessionResponse { diff --git a/proto/agynio/api/terminal_proxy/v1/terminal_proxy.proto b/proto/agynio/api/terminal_proxy/v1/terminal_proxy.proto index 1c1b2c8..bfab652 100644 --- a/proto/agynio/api/terminal_proxy/v1/terminal_proxy.proto +++ b/proto/agynio/api/terminal_proxy/v1/terminal_proxy.proto @@ -18,7 +18,7 @@ enum SessionKind { // ends up in a ticket. SESSION_KIND_UNSPECIFIED = 0; - // A login shell on a PTY, with the platform binaries on PATH. + // A login shell on a PTY. The PTY closes with the connection. SESSION_KIND_SHELL = 1; // A caller-supplied command on a PTY. Carries no more privilege than SHELL — @@ -28,6 +28,11 @@ enum SessionKind { // The workspace sync endpoint. Non-TTY: no PTY, no resize, and stdout kept // separate from stderr so diagnostics cannot corrupt protocol frames. SESSION_KIND_SYNC = 3; + + // A PTY on a named shell that outlives the session. Attaches to shell_id, + // creating it when it does not exist. A second session on the same shell + // displaces the first. + SESSION_KIND_SHELL_ATTACH = 4; } message IssueTicketRequest { @@ -48,6 +53,15 @@ message IssueTicketRequest { // absolute; it is normalized before being bound into the ticket, and // resolved against the real filesystem inside the container. string sync_root = 6; + + // Names the shell. Required when kind is SHELL_ATTACH, ignored otherwise. + // Client-generated and opaque, matching ^[A-Za-z0-9_-]{1,64}$. + string shell_id = 7; + + // Working directory for the shell. Optional; meaningful only when kind is + // SHELL_ATTACH. Applied when the shell is created and ignored when it + // already exists. Must be absolute. + string shell_cwd = 8; } message IssueTicketResponse {