From 638bb79428a82187597ef0d5a1bfdef14f3ddd1e Mon Sep 17 00:00:00 2001 From: Rowan Stein Date: Thu, 26 Feb 2026 14:44:58 +0000 Subject: [PATCH 01/20] feat(apss): add AgentStateService protobuf schema (conversationId-only, seq+version, text body now, extensible) --- .../api/apss/v1/agent_state_service.proto | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 proto/agynio/api/apss/v1/agent_state_service.proto diff --git a/proto/agynio/api/apss/v1/agent_state_service.proto b/proto/agynio/api/apss/v1/agent_state_service.proto new file mode 100644 index 0000000..50d3fd1 --- /dev/null +++ b/proto/agynio/api/apss/v1/agent_state_service.proto @@ -0,0 +1,167 @@ +syntax = "proto3"; + +package agynio.api.apss.v1; + +import "google/protobuf/timestamp.proto"; + +option java_multiple_files = true; +option java_outer_classname = "AgentStateServiceProto"; +option go_package = "github.com/agynio/api/gen/agynio/api/apss/v1;apssv1"; + +// Agent Persistent State Service (APSS) +// - conversationId-only API (threadId/nodeId mapping handled outside) +// - ordered messages via monotonic seq per conversation +// - conversation-level version for optimistic concurrency (CAS/ETag) +// - message bodies are well-typed; text-only now, extensible via oneof +// - no external type references + +service AgentStateService { + // Append messages (batch). Server assigns seq; bumps conversation version. + rpc AppendMessages(AppendMessagesRequest) returns (AppendMessagesResponse); + + // List messages with filtering & pagination. + rpc ListMessages(ListMessagesRequest) returns (ListMessagesResponse); + + // Replace-by-kind: upsert latest active message per kind (e.g., system/summary/memory). + rpc ReplaceByKind(ReplaceByKindRequest) returns (ReplaceByKindResponse); + + // Trim messages by seq cutoff (archive/delete older messages). + rpc TrimBySeq(TrimBySeqRequest) returns (TrimBySeqResponse); + + // Get conversation metadata (version, last_seq, message_count). + rpc GetConversationMetadata(GetConversationMetadataRequest) returns (GetConversationMetadataResponse); +} + +enum Role { + ROLE_UNSPECIFIED = 0; + ROLE_USER = 1; + ROLE_ASSISTANT = 2; + ROLE_TOOL = 3; +} + +// Optional attributes for memory-like messages. +message MemoryAttrs { + enum Place { + PLACE_UNSPECIFIED = 0; + PLACE_AFTER_SYSTEM = 1; + PLACE_LAST_MESSAGE = 2; + } + Place place = 1; +} + +// Text payload for now. Future extensions can add more oneof variants. +message TextBody { string text = 1; } + +message MessageBody { + oneof body { + TextBody text = 1; + // Reserved for future extensions: + // ImageBody image = 2; + // AudioBody audio = 3; + } +} + +// Core message entity stored by APSS. +message Message { + string id = 1; // server-assigned UUID + string conversation_id = 2; // routing key + int64 seq = 3; // monotonic per conversation + Role role = 4; // user | assistant | tool + string kind = 5; // free-form tag (e.g., system, summary, memory, restriction, tool_output) + MessageBody body = 6; // payload + MemoryAttrs memory = 7; // optional placement attributes (for memory kinds) + google.protobuf.Timestamp created_at = 8; + bool archived = 9; // soft delete / superseded flag + google.protobuf.Timestamp archived_at = 10; +} + +message ConversationMetadata { + string conversation_id = 1; + int64 version = 2; // CAS token (monotonic) + int64 last_seq = 3; // highest seq among active messages + int64 message_count = 4; // count of active messages + google.protobuf.Timestamp updated_at = 5; +} + +// Append +message AppendMessageInput { + Role role = 1; + string kind = 2; + MessageBody body = 3; + MemoryAttrs memory = 4; // optional, meaningful for memory kinds +} + +message AppendMessagesRequest { + string conversation_id = 1; + int64 expect_version = 2; // optimistic concurrency; 0 to bypass on create + string idempotency_key = 3; // dedupe retries + repeated AppendMessageInput messages = 4; +} + +message AppendMessagesResponse { + ConversationMetadata metadata = 1; + repeated Message appended = 2; +} + +// List +message ListMessagesRequest { + string conversation_id = 1; + repeated Role roles = 2; // filter roles (optional) + repeated string kinds = 3; // filter kinds (optional) + int64 seq_gte = 4; // inclusive lower bound (optional) + int64 seq_lte = 5; // inclusive upper bound (optional) + bool include_archived = 6; // default false + int32 page_size = 7; // server may cap + string page_token = 8; // opaque cursor + bool descending = 9; // default ascending +} + +message ListMessagesResponse { + ConversationMetadata metadata = 1; + repeated Message messages = 2; + string next_page_token = 3; +} + +// Replace-by-kind +message ReplaceByKindInput { + string kind = 1; + Role role = 2; + MessageBody body = 3; + MemoryAttrs memory = 4; // optional +} + +message ReplaceByKindRequest { + string conversation_id = 1; + int64 expect_version = 2; + string idempotency_key = 3; + repeated ReplaceByKindInput replacements = 4; + bool archive_prior = 5; // recommended true +} + +message ReplaceByKindResponse { + ConversationMetadata metadata = 1; + repeated Message replaced = 2; + repeated Message archived = 3; +} + +// Trim +message TrimBySeqRequest { + string conversation_id = 1; + int64 expect_version = 2; + string idempotency_key = 3; + int64 cutoff_seq = 4; // inclusive + bool archive_only = 5; // soft trim vs hard delete + bool keep_latest_special_kinds = 6; + repeated string special_kinds = 7; // e.g., system, summary, memory +} + +message TrimBySeqResponse { + ConversationMetadata metadata = 1; + int64 trimmed_count = 2; + int64 trimmed_through_seq = 3; + repeated Message kept_special = 4; +} + +// Metadata +message GetConversationMetadataRequest { string conversation_id = 1; } +message GetConversationMetadataResponse { ConversationMetadata metadata = 1; } From 7e9b3a7274f320948943d905d264563f7ed43afe Mon Sep 17 00:00:00 2001 From: Rowan Stein Date: Thu, 26 Feb 2026 15:35:44 +0000 Subject: [PATCH 02/20] chore(agent_state): move APSS proto to proto/agynio/api/agent_state/v1 and rename package/go_package --- .../api/{apss => agent_state}/v1/agent_state_service.proto | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename proto/agynio/api/{apss => agent_state}/v1/agent_state_service.proto (97%) diff --git a/proto/agynio/api/apss/v1/agent_state_service.proto b/proto/agynio/api/agent_state/v1/agent_state_service.proto similarity index 97% rename from proto/agynio/api/apss/v1/agent_state_service.proto rename to proto/agynio/api/agent_state/v1/agent_state_service.proto index 50d3fd1..56a5c5e 100644 --- a/proto/agynio/api/apss/v1/agent_state_service.proto +++ b/proto/agynio/api/agent_state/v1/agent_state_service.proto @@ -1,12 +1,12 @@ syntax = "proto3"; -package agynio.api.apss.v1; +package agynio.api.agent_state.v1; import "google/protobuf/timestamp.proto"; option java_multiple_files = true; option java_outer_classname = "AgentStateServiceProto"; -option go_package = "github.com/agynio/api/gen/agynio/api/apss/v1;apssv1"; +option go_package = "github.com/agynio/api/gen/agynio/api/agent_state/v1;agentstatev1"; // Agent Persistent State Service (APSS) // - conversationId-only API (threadId/nodeId mapping handled outside) From 7eb9893c9d98d13641d46e383968badf81e3342d Mon Sep 17 00:00:00 2001 From: Rowan Stein Date: Thu, 26 Feb 2026 15:53:25 +0000 Subject: [PATCH 03/20] chore(agent_state): rename proto file to agent_state.proto; rename service to AgentState; update java_outer_classname --- .../v1/{agent_state_service.proto => agent_state.proto} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename proto/agynio/api/agent_state/v1/{agent_state_service.proto => agent_state.proto} (98%) diff --git a/proto/agynio/api/agent_state/v1/agent_state_service.proto b/proto/agynio/api/agent_state/v1/agent_state.proto similarity index 98% rename from proto/agynio/api/agent_state/v1/agent_state_service.proto rename to proto/agynio/api/agent_state/v1/agent_state.proto index 56a5c5e..f1a49cc 100644 --- a/proto/agynio/api/agent_state/v1/agent_state_service.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -5,7 +5,7 @@ package agynio.api.agent_state.v1; import "google/protobuf/timestamp.proto"; option java_multiple_files = true; -option java_outer_classname = "AgentStateServiceProto"; +option java_outer_classname = "AgentStateProto"; option go_package = "github.com/agynio/api/gen/agynio/api/agent_state/v1;agentstatev1"; // Agent Persistent State Service (APSS) @@ -15,7 +15,7 @@ option go_package = "github.com/agynio/api/gen/agynio/api/agent_state/v1;agentst // - message bodies are well-typed; text-only now, extensible via oneof // - no external type references -service AgentStateService { +service AgentState { // Append messages (batch). Server assigns seq; bumps conversation version. rpc AppendMessages(AppendMessagesRequest) returns (AppendMessagesResponse); From 77d28c686b6fd044ac65602a9988dee2761a2c9c Mon Sep 17 00:00:00 2001 From: Rowan Stein Date: Thu, 26 Feb 2026 17:20:36 +0000 Subject: [PATCH 04/20] fix(agent_state): restore AgentStateService name to satisfy buf STANDARD lint --- proto/agynio/api/agent_state/v1/agent_state.proto | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proto/agynio/api/agent_state/v1/agent_state.proto b/proto/agynio/api/agent_state/v1/agent_state.proto index f1a49cc..56a5c5e 100644 --- a/proto/agynio/api/agent_state/v1/agent_state.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -5,7 +5,7 @@ package agynio.api.agent_state.v1; import "google/protobuf/timestamp.proto"; option java_multiple_files = true; -option java_outer_classname = "AgentStateProto"; +option java_outer_classname = "AgentStateServiceProto"; option go_package = "github.com/agynio/api/gen/agynio/api/agent_state/v1;agentstatev1"; // Agent Persistent State Service (APSS) @@ -15,7 +15,7 @@ option go_package = "github.com/agynio/api/gen/agynio/api/agent_state/v1;agentst // - message bodies are well-typed; text-only now, extensible via oneof // - no external type references -service AgentState { +service AgentStateService { // Append messages (batch). Server assigns seq; bumps conversation version. rpc AppendMessages(AppendMessagesRequest) returns (AppendMessagesResponse); From dc4d3bf7a82efdcad8105a07de1f3ce02e043ee2 Mon Sep 17 00:00:00 2001 From: Rowan Stein Date: Thu, 26 Feb 2026 18:29:09 +0000 Subject: [PATCH 05/20] chore(agent_state): remove Java-specific protobuf options for clean, language-agnostic schema --- proto/agynio/api/agent_state/v1/agent_state.proto | 2 -- 1 file changed, 2 deletions(-) diff --git a/proto/agynio/api/agent_state/v1/agent_state.proto b/proto/agynio/api/agent_state/v1/agent_state.proto index 56a5c5e..f9cbe0d 100644 --- a/proto/agynio/api/agent_state/v1/agent_state.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -4,8 +4,6 @@ package agynio.api.agent_state.v1; import "google/protobuf/timestamp.proto"; -option java_multiple_files = true; -option java_outer_classname = "AgentStateServiceProto"; option go_package = "github.com/agynio/api/gen/agynio/api/agent_state/v1;agentstatev1"; // Agent Persistent State Service (APSS) From ac9f36c4e5f040af34e04ee755593111e6a467ce Mon Sep 17 00:00:00 2001 From: Rowan Stein Date: Thu, 26 Feb 2026 20:05:27 +0000 Subject: [PATCH 06/20] feat(agent_state): add CreateSnapshotFromIds RPC with materialized ContextSnapshot and integrity hash --- .../api/agent_state/v1/agent_state.proto | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/proto/agynio/api/agent_state/v1/agent_state.proto b/proto/agynio/api/agent_state/v1/agent_state.proto index f9cbe0d..0d240fd 100644 --- a/proto/agynio/api/agent_state/v1/agent_state.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -28,6 +28,12 @@ service AgentStateService { // Get conversation metadata (version, last_seq, message_count). rpc GetConversationMetadata(GetConversationMetadataRequest) returns (GetConversationMetadataResponse); + + // Create a materialized context snapshot for a specific LLM call. + // The client supplies the exact message IDs and call_id; APSS stores + // a materialized copy of each message body in the provided order. + // This guarantees reproducibility even after trims/edits. + rpc CreateSnapshotFromIds(CreateSnapshotFromIdsRequest) returns (CreateSnapshotFromIdsResponse); } enum Role { @@ -163,3 +169,44 @@ message TrimBySeqResponse { // Metadata message GetConversationMetadataRequest { string conversation_id = 1; } message GetConversationMetadataResponse { ConversationMetadata metadata = 1; } + + // Create a materialized context snapshot for a specific LLM call. + // The client supplies the exact message IDs and call_id; APSS stores + // a materialized copy of each message body in the provided order. + // This guarantees reproducibility even after trims/edits. + rpc CreateSnapshotFromIds(CreateSnapshotFromIdsRequest) returns (CreateSnapshotFromIdsResponse); + +// -------------------------- +// Context Snapshots +// -------------------------- + +// A materialized snapshot of the exact context sent to an LLM call. +message ContextSnapshot { + string snapshot_id = 1; // server-assigned UUID + string conversation_id = 2; // conversation this snapshot belongs to + string call_id = 3; // client-supplied identifier for the LLM call + google.protobuf.Timestamp created_at = 4; + int64 version_at_snapshot = 5; // conversation version at snapshot time + repeated SnapshotItem items = 6; // ordered exactly as provided +} + +// Each snapshot item is a materialized message copy with integrity hash. +message SnapshotItem { + string message_id = 1; // original message id + int64 seq = 2; // original message seq + Role role = 3; // original role + string kind = 4; // original kind + MessageBody body = 5; // materialized body copy + string body_sha256 = 6; // integrity hash of the serialized body +} + +message CreateSnapshotFromIdsRequest { + string conversation_id = 1; + string call_id = 2; // correlation id for the LLM call + repeated string message_ids = 3; // exact order to persist in the snapshot + int64 expect_version = 4; // optional CAS to ensure snapshot matches intended state +} + +message CreateSnapshotFromIdsResponse { + ContextSnapshot snapshot = 1; +} From 613cd54d6b02c29f84bcc7d85f5f9c74ca694058 Mon Sep 17 00:00:00 2001 From: Rowan Stein Date: Thu, 26 Feb 2026 20:06:47 +0000 Subject: [PATCH 07/20] feat(agent_state): add CreateSnapshotFromIds RPC inside service; materialized snapshot messages --- proto/agynio/api/agent_state/v1/agent_state.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/agynio/api/agent_state/v1/agent_state.proto b/proto/agynio/api/agent_state/v1/agent_state.proto index 0d240fd..18416dd 100644 --- a/proto/agynio/api/agent_state/v1/agent_state.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -34,6 +34,7 @@ service AgentStateService { // a materialized copy of each message body in the provided order. // This guarantees reproducibility even after trims/edits. rpc CreateSnapshotFromIds(CreateSnapshotFromIdsRequest) returns (CreateSnapshotFromIdsResponse); + } enum Role { @@ -174,7 +175,6 @@ message GetConversationMetadataResponse { ConversationMetadata metadata = 1; } // The client supplies the exact message IDs and call_id; APSS stores // a materialized copy of each message body in the provided order. // This guarantees reproducibility even after trims/edits. - rpc CreateSnapshotFromIds(CreateSnapshotFromIdsRequest) returns (CreateSnapshotFromIdsResponse); // -------------------------- // Context Snapshots From 473bba7577c61f5f950a12a4c6b4c38232fc25d2 Mon Sep 17 00:00:00 2001 From: Rowan Stein Date: Thu, 26 Feb 2026 20:29:39 +0000 Subject: [PATCH 08/20] refactor(agent_state): rename CreateSnapshotFromIds to CreateSnapshot; remove call_id from request and snapshot; keep exact-order ids --- proto/agynio/api/agent_state/v1/agent_state.proto | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/proto/agynio/api/agent_state/v1/agent_state.proto b/proto/agynio/api/agent_state/v1/agent_state.proto index 18416dd..e039227 100644 --- a/proto/agynio/api/agent_state/v1/agent_state.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -33,7 +33,7 @@ service AgentStateService { // The client supplies the exact message IDs and call_id; APSS stores // a materialized copy of each message body in the provided order. // This guarantees reproducibility even after trims/edits. - rpc CreateSnapshotFromIds(CreateSnapshotFromIdsRequest) returns (CreateSnapshotFromIdsResponse); + rpc CreateSnapshot(CreateSnapshotRequest) returns (CreateSnapshotResponse); } @@ -184,7 +184,6 @@ message GetConversationMetadataResponse { ConversationMetadata metadata = 1; } message ContextSnapshot { string snapshot_id = 1; // server-assigned UUID string conversation_id = 2; // conversation this snapshot belongs to - string call_id = 3; // client-supplied identifier for the LLM call google.protobuf.Timestamp created_at = 4; int64 version_at_snapshot = 5; // conversation version at snapshot time repeated SnapshotItem items = 6; // ordered exactly as provided @@ -200,13 +199,12 @@ message SnapshotItem { string body_sha256 = 6; // integrity hash of the serialized body } -message CreateSnapshotFromIdsRequest { +message CreateSnapshotRequest { string conversation_id = 1; - string call_id = 2; // correlation id for the LLM call - repeated string message_ids = 3; // exact order to persist in the snapshot - int64 expect_version = 4; // optional CAS to ensure snapshot matches intended state + repeated string message_ids = 2; // exact order to persist in the snapshot + int64 expect_version = 3; // optional CAS to ensure snapshot matches intended state } -message CreateSnapshotFromIdsResponse { +message CreateSnapshotResponse { ContextSnapshot snapshot = 1; } From 2db95bbfd5abe499d897f1be2104fa55c40ce305 Mon Sep 17 00:00:00 2001 From: Rowan Stein Date: Thu, 26 Feb 2026 23:13:03 +0000 Subject: [PATCH 09/20] feat(agent_state): simplify CreateSnapshot (remove expect_version); simplify ContextSnapshot; add listing RPCs for snapshot messages and IDs, and conversation message IDs --- .../api/agent_state/v1/agent_state.proto | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/proto/agynio/api/agent_state/v1/agent_state.proto b/proto/agynio/api/agent_state/v1/agent_state.proto index e039227..42c2215 100644 --- a/proto/agynio/api/agent_state/v1/agent_state.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -185,8 +185,6 @@ message ContextSnapshot { string snapshot_id = 1; // server-assigned UUID string conversation_id = 2; // conversation this snapshot belongs to google.protobuf.Timestamp created_at = 4; - int64 version_at_snapshot = 5; // conversation version at snapshot time - repeated SnapshotItem items = 6; // ordered exactly as provided } // Each snapshot item is a materialized message copy with integrity hash. @@ -202,9 +200,46 @@ message SnapshotItem { message CreateSnapshotRequest { string conversation_id = 1; repeated string message_ids = 2; // exact order to persist in the snapshot - int64 expect_version = 3; // optional CAS to ensure snapshot matches intended state } message CreateSnapshotResponse { ContextSnapshot snapshot = 1; } + +// Snapshot listing RPCs +message ListSnapshotMessagesRequest { + string snapshot_id = 1; + int32 page_size = 2; + string page_token = 3; +} + +message ListSnapshotMessagesResponse { + repeated Message messages = 1; + string next_page_token = 2; +} + +message ListSnapshotMessageIdsRequest { + string snapshot_id = 1; + int32 page_size = 2; + string page_token = 3; +} + +message ListSnapshotMessageIdsResponse { + repeated string message_ids = 1; + string next_page_token = 2; +} + +message ListConversationMessageIdsRequest { + string conversation_id = 1; + int64 seq_gte = 2; + int64 seq_lte = 3; + bool include_archived = 4; + int32 page_size = 5; + string page_token = 6; + bool descending = 7; +} + +message ListConversationMessageIdsResponse { + repeated string message_ids = 1; + string next_page_token = 2; +} From bfb0333c8193dd6ce91c3b6192aa521e6ee06b15 Mon Sep 17 00:00:00 2001 From: Rowan Stein Date: Fri, 27 Feb 2026 01:42:57 +0000 Subject: [PATCH 10/20] feat(agent_state): add RPCs to list snapshot messages, snapshot ids, and conversation message ids --- proto/agynio/api/agent_state/v1/agent_state.proto | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/proto/agynio/api/agent_state/v1/agent_state.proto b/proto/agynio/api/agent_state/v1/agent_state.proto index 42c2215..b5d07f8 100644 --- a/proto/agynio/api/agent_state/v1/agent_state.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -29,6 +29,15 @@ service AgentStateService { // Get conversation metadata (version, last_seq, message_count). rpc GetConversationMetadata(GetConversationMetadataRequest) returns (GetConversationMetadataResponse); + // List messages in a snapshot (exact stored order, paginated). + rpc ListSnapshotMessages(ListSnapshotMessagesRequest) returns (ListSnapshotMessagesResponse); + + // List message IDs in a snapshot (exact stored order, paginated). + rpc ListSnapshotMessageIds(ListSnapshotMessageIdsRequest) returns (ListSnapshotMessageIdsResponse); + + // List message IDs in a conversation (filtered, paginated). + rpc ListConversationMessageIds(ListConversationMessageIdsRequest) returns (ListConversationMessageIdsResponse); + // Create a materialized context snapshot for a specific LLM call. // The client supplies the exact message IDs and call_id; APSS stores // a materialized copy of each message body in the provided order. From b0861d3808ed185e4c2c9a1f5f7ba1aeda98ee7e Mon Sep 17 00:00:00 2001 From: Casey Brooks Date: Fri, 27 Feb 2026 02:15:56 +0000 Subject: [PATCH 11/20] refactor(agent_state): rename conversation rpcs --- .../api/agent_state/v1/agent_state.proto | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/proto/agynio/api/agent_state/v1/agent_state.proto b/proto/agynio/api/agent_state/v1/agent_state.proto index b5d07f8..994071c 100644 --- a/proto/agynio/api/agent_state/v1/agent_state.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -15,19 +15,19 @@ option go_package = "github.com/agynio/api/gen/agynio/api/agent_state/v1;agentst service AgentStateService { // Append messages (batch). Server assigns seq; bumps conversation version. - rpc AppendMessages(AppendMessagesRequest) returns (AppendMessagesResponse); + rpc AppendConversationMessages(AppendConversationMessagesRequest) returns (AppendConversationMessagesResponse); // List messages with filtering & pagination. - rpc ListMessages(ListMessagesRequest) returns (ListMessagesResponse); + rpc ListConversationMessages(ListConversationMessagesRequest) returns (ListConversationMessagesResponse); // Replace-by-kind: upsert latest active message per kind (e.g., system/summary/memory). - rpc ReplaceByKind(ReplaceByKindRequest) returns (ReplaceByKindResponse); + rpc ReplaceConversationMessagesByKind(ReplaceConversationMessagesByKindRequest) returns (ReplaceConversationMessagesByKindResponse); // Trim messages by seq cutoff (archive/delete older messages). - rpc TrimBySeq(TrimBySeqRequest) returns (TrimBySeqResponse); + rpc TrimConversationMessagesBySeq(TrimConversationMessagesBySeqRequest) returns (TrimConversationMessagesBySeqResponse); // Get conversation metadata (version, last_seq, message_count). - rpc GetConversationMetadata(GetConversationMetadataRequest) returns (GetConversationMetadataResponse); + rpc GetConversation(GetConversationRequest) returns (GetConversationResponse); // List messages in a snapshot (exact stored order, paginated). rpc ListSnapshotMessages(ListSnapshotMessagesRequest) returns (ListSnapshotMessagesResponse); @@ -39,7 +39,7 @@ service AgentStateService { rpc ListConversationMessageIds(ListConversationMessageIdsRequest) returns (ListConversationMessageIdsResponse); // Create a materialized context snapshot for a specific LLM call. - // The client supplies the exact message IDs and call_id; APSS stores + // The client supplies the exact message IDs; APSS stores // a materialized copy of each message body in the provided order. // This guarantees reproducibility even after trims/edits. rpc CreateSnapshot(CreateSnapshotRequest) returns (CreateSnapshotResponse); @@ -105,20 +105,20 @@ message AppendMessageInput { MemoryAttrs memory = 4; // optional, meaningful for memory kinds } -message AppendMessagesRequest { +message AppendConversationMessagesRequest { string conversation_id = 1; int64 expect_version = 2; // optimistic concurrency; 0 to bypass on create string idempotency_key = 3; // dedupe retries repeated AppendMessageInput messages = 4; } -message AppendMessagesResponse { +message AppendConversationMessagesResponse { ConversationMetadata metadata = 1; repeated Message appended = 2; } // List -message ListMessagesRequest { +message ListConversationMessagesRequest { string conversation_id = 1; repeated Role roles = 2; // filter roles (optional) repeated string kinds = 3; // filter kinds (optional) @@ -130,7 +130,7 @@ message ListMessagesRequest { bool descending = 9; // default ascending } -message ListMessagesResponse { +message ListConversationMessagesResponse { ConversationMetadata metadata = 1; repeated Message messages = 2; string next_page_token = 3; @@ -144,7 +144,7 @@ message ReplaceByKindInput { MemoryAttrs memory = 4; // optional } -message ReplaceByKindRequest { +message ReplaceConversationMessagesByKindRequest { string conversation_id = 1; int64 expect_version = 2; string idempotency_key = 3; @@ -152,14 +152,14 @@ message ReplaceByKindRequest { bool archive_prior = 5; // recommended true } -message ReplaceByKindResponse { +message ReplaceConversationMessagesByKindResponse { ConversationMetadata metadata = 1; repeated Message replaced = 2; repeated Message archived = 3; } // Trim -message TrimBySeqRequest { +message TrimConversationMessagesBySeqRequest { string conversation_id = 1; int64 expect_version = 2; string idempotency_key = 3; @@ -169,7 +169,7 @@ message TrimBySeqRequest { repeated string special_kinds = 7; // e.g., system, summary, memory } -message TrimBySeqResponse { +message TrimConversationMessagesBySeqResponse { ConversationMetadata metadata = 1; int64 trimmed_count = 2; int64 trimmed_through_seq = 3; @@ -177,13 +177,13 @@ message TrimBySeqResponse { } // Metadata -message GetConversationMetadataRequest { string conversation_id = 1; } -message GetConversationMetadataResponse { ConversationMetadata metadata = 1; } +message GetConversationRequest { string conversation_id = 1; } +message GetConversationResponse { ConversationMetadata metadata = 1; } - // Create a materialized context snapshot for a specific LLM call. - // The client supplies the exact message IDs and call_id; APSS stores - // a materialized copy of each message body in the provided order. - // This guarantees reproducibility even after trims/edits. +// Create a materialized context snapshot for a specific LLM call. +// The client supplies the exact message IDs; APSS stores +// a materialized copy of each message body in the provided order. +// This guarantees reproducibility even after trims/edits. // -------------------------- // Context Snapshots From 86c3dae3afaf45c74f004dc5ea94774588753956 Mon Sep 17 00:00:00 2001 From: Casey Brooks Date: Fri, 27 Feb 2026 02:21:34 +0000 Subject: [PATCH 12/20] feat(agent_state): add insert delete conversation rpcs --- .../api/agent_state/v1/agent_state.proto | 55 ++++++++----------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/proto/agynio/api/agent_state/v1/agent_state.proto b/proto/agynio/api/agent_state/v1/agent_state.proto index 994071c..2c16f61 100644 --- a/proto/agynio/api/agent_state/v1/agent_state.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -20,24 +20,24 @@ service AgentStateService { // List messages with filtering & pagination. rpc ListConversationMessages(ListConversationMessagesRequest) returns (ListConversationMessagesResponse); - // Replace-by-kind: upsert latest active message per kind (e.g., system/summary/memory). - rpc ReplaceConversationMessagesByKind(ReplaceConversationMessagesByKindRequest) returns (ReplaceConversationMessagesByKindResponse); + // Delete messages by id. + rpc DeleteConversationMessages(DeleteConversationMessagesRequest) returns (DeleteConversationMessagesResponse); - // Trim messages by seq cutoff (archive/delete older messages). - rpc TrimConversationMessagesBySeq(TrimConversationMessagesBySeqRequest) returns (TrimConversationMessagesBySeqResponse); + // Insert messages relative to an anchor seq. + rpc InsertConversationMessages(InsertConversationMessagesRequest) returns (InsertConversationMessagesResponse); // Get conversation metadata (version, last_seq, message_count). rpc GetConversation(GetConversationRequest) returns (GetConversationResponse); + // List message IDs in a conversation (filtered, paginated). + rpc ListConversationMessageIds(ListConversationMessageIdsRequest) returns (ListConversationMessageIdsResponse); + // List messages in a snapshot (exact stored order, paginated). rpc ListSnapshotMessages(ListSnapshotMessagesRequest) returns (ListSnapshotMessagesResponse); // List message IDs in a snapshot (exact stored order, paginated). rpc ListSnapshotMessageIds(ListSnapshotMessageIdsRequest) returns (ListSnapshotMessageIdsResponse); - // List message IDs in a conversation (filtered, paginated). - rpc ListConversationMessageIds(ListConversationMessageIdsRequest) returns (ListConversationMessageIdsResponse); - // Create a materialized context snapshot for a specific LLM call. // The client supplies the exact message IDs; APSS stores // a materialized copy of each message body in the provided order. @@ -136,44 +136,37 @@ message ListConversationMessagesResponse { string next_page_token = 3; } -// Replace-by-kind -message ReplaceByKindInput { - string kind = 1; - Role role = 2; - MessageBody body = 3; - MemoryAttrs memory = 4; // optional -} - -message ReplaceConversationMessagesByKindRequest { +// Delete +message DeleteConversationMessagesRequest { string conversation_id = 1; int64 expect_version = 2; string idempotency_key = 3; - repeated ReplaceByKindInput replacements = 4; - bool archive_prior = 5; // recommended true + repeated string message_ids = 4; } -message ReplaceConversationMessagesByKindResponse { +message DeleteConversationMessagesResponse { ConversationMetadata metadata = 1; - repeated Message replaced = 2; - repeated Message archived = 3; + repeated string deleted_message_ids = 2; } -// Trim -message TrimConversationMessagesBySeqRequest { +// Insert +message InsertConversationMessagesRequest { + enum Placement { + PLACEMENT_UNSPECIFIED = 0; + PLACEMENT_BEFORE = 1; + PLACEMENT_AFTER = 2; + } string conversation_id = 1; int64 expect_version = 2; string idempotency_key = 3; - int64 cutoff_seq = 4; // inclusive - bool archive_only = 5; // soft trim vs hard delete - bool keep_latest_special_kinds = 6; - repeated string special_kinds = 7; // e.g., system, summary, memory + int64 anchor_seq = 4; + Placement placement = 5; + repeated AppendMessageInput messages = 6; } -message TrimConversationMessagesBySeqResponse { +message InsertConversationMessagesResponse { ConversationMetadata metadata = 1; - int64 trimmed_count = 2; - int64 trimmed_through_seq = 3; - repeated Message kept_special = 4; + repeated Message inserted = 2; } // Metadata From 26be51a917e6747a6b8131b13d915694a372eaa6 Mon Sep 17 00:00:00 2001 From: Casey Brooks Date: Fri, 27 Feb 2026 02:42:04 +0000 Subject: [PATCH 13/20] refactor(agent_state): prune deprecated fields --- .../api/agent_state/v1/agent_state.proto | 62 ++++++------------- 1 file changed, 19 insertions(+), 43 deletions(-) diff --git a/proto/agynio/api/agent_state/v1/agent_state.proto b/proto/agynio/api/agent_state/v1/agent_state.proto index 2c16f61..3007511 100644 --- a/proto/agynio/api/agent_state/v1/agent_state.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -53,16 +53,6 @@ enum Role { ROLE_TOOL = 3; } -// Optional attributes for memory-like messages. -message MemoryAttrs { - enum Place { - PLACE_UNSPECIFIED = 0; - PLACE_AFTER_SYSTEM = 1; - PLACE_LAST_MESSAGE = 2; - } - Place place = 1; -} - // Text payload for now. Future extensions can add more oneof variants. message TextBody { string text = 1; } @@ -79,22 +69,17 @@ message MessageBody { message Message { string id = 1; // server-assigned UUID string conversation_id = 2; // routing key - int64 seq = 3; // monotonic per conversation - Role role = 4; // user | assistant | tool - string kind = 5; // free-form tag (e.g., system, summary, memory, restriction, tool_output) - MessageBody body = 6; // payload - MemoryAttrs memory = 7; // optional placement attributes (for memory kinds) - google.protobuf.Timestamp created_at = 8; - bool archived = 9; // soft delete / superseded flag - google.protobuf.Timestamp archived_at = 10; + Role role = 3; // user | assistant | tool + string kind = 4; // free-form tag (e.g., system, summary, memory, restriction, tool_output) + MessageBody body = 5; // payload + google.protobuf.Timestamp created_at = 6; } message ConversationMetadata { string conversation_id = 1; int64 version = 2; // CAS token (monotonic) - int64 last_seq = 3; // highest seq among active messages - int64 message_count = 4; // count of active messages - google.protobuf.Timestamp updated_at = 5; + int64 message_count = 3; // count of active messages + google.protobuf.Timestamp updated_at = 4; } // Append @@ -102,14 +87,12 @@ message AppendMessageInput { Role role = 1; string kind = 2; MessageBody body = 3; - MemoryAttrs memory = 4; // optional, meaningful for memory kinds } message AppendConversationMessagesRequest { string conversation_id = 1; int64 expect_version = 2; // optimistic concurrency; 0 to bypass on create - string idempotency_key = 3; // dedupe retries - repeated AppendMessageInput messages = 4; + repeated AppendMessageInput messages = 3; } message AppendConversationMessagesResponse { @@ -124,10 +107,8 @@ message ListConversationMessagesRequest { repeated string kinds = 3; // filter kinds (optional) int64 seq_gte = 4; // inclusive lower bound (optional) int64 seq_lte = 5; // inclusive upper bound (optional) - bool include_archived = 6; // default false - int32 page_size = 7; // server may cap - string page_token = 8; // opaque cursor - bool descending = 9; // default ascending + int32 page_size = 6; // server may cap + string page_token = 7; // opaque cursor } message ListConversationMessagesResponse { @@ -140,8 +121,7 @@ message ListConversationMessagesResponse { message DeleteConversationMessagesRequest { string conversation_id = 1; int64 expect_version = 2; - string idempotency_key = 3; - repeated string message_ids = 4; + repeated string message_ids = 3; } message DeleteConversationMessagesResponse { @@ -158,10 +138,9 @@ message InsertConversationMessagesRequest { } string conversation_id = 1; int64 expect_version = 2; - string idempotency_key = 3; - int64 anchor_seq = 4; - Placement placement = 5; - repeated AppendMessageInput messages = 6; + int64 anchor_seq = 3; + Placement placement = 4; + repeated AppendMessageInput messages = 5; } message InsertConversationMessagesResponse { @@ -192,11 +171,10 @@ message ContextSnapshot { // Each snapshot item is a materialized message copy with integrity hash. message SnapshotItem { string message_id = 1; // original message id - int64 seq = 2; // original message seq - Role role = 3; // original role - string kind = 4; // original kind - MessageBody body = 5; // materialized body copy - string body_sha256 = 6; // integrity hash of the serialized body + Role role = 2; // original role + string kind = 3; // original kind + MessageBody body = 4; // materialized body copy + string body_sha256 = 5; // integrity hash of the serialized body } message CreateSnapshotRequest { @@ -235,10 +213,8 @@ message ListConversationMessageIdsRequest { string conversation_id = 1; int64 seq_gte = 2; int64 seq_lte = 3; - bool include_archived = 4; - int32 page_size = 5; - string page_token = 6; - bool descending = 7; + int32 page_size = 4; + string page_token = 5; } message ListConversationMessageIdsResponse { From 941c3ca057de297a935aa7e8670e492b0d32728d Mon Sep 17 00:00:00 2001 From: Casey Brooks Date: Fri, 27 Feb 2026 02:52:26 +0000 Subject: [PATCH 14/20] refactor(agent_state): drop deprecated metadata fields --- .../api/agent_state/v1/agent_state.proto | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/proto/agynio/api/agent_state/v1/agent_state.proto b/proto/agynio/api/agent_state/v1/agent_state.proto index 3007511..eabf518 100644 --- a/proto/agynio/api/agent_state/v1/agent_state.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -8,13 +8,11 @@ option go_package = "github.com/agynio/api/gen/agynio/api/agent_state/v1;agentst // Agent Persistent State Service (APSS) // - conversationId-only API (threadId/nodeId mapping handled outside) -// - ordered messages via monotonic seq per conversation -// - conversation-level version for optimistic concurrency (CAS/ETag) // - message bodies are well-typed; text-only now, extensible via oneof // - no external type references service AgentStateService { - // Append messages (batch). Server assigns seq; bumps conversation version. + // Append messages (batch). rpc AppendConversationMessages(AppendConversationMessagesRequest) returns (AppendConversationMessagesResponse); // List messages with filtering & pagination. @@ -23,10 +21,10 @@ service AgentStateService { // Delete messages by id. rpc DeleteConversationMessages(DeleteConversationMessagesRequest) returns (DeleteConversationMessagesResponse); - // Insert messages relative to an anchor seq. + // Insert messages using placement hints. rpc InsertConversationMessages(InsertConversationMessagesRequest) returns (InsertConversationMessagesResponse); - // Get conversation metadata (version, last_seq, message_count). + // Get conversation metadata (message counts and timestamps). rpc GetConversation(GetConversationRequest) returns (GetConversationResponse); // List message IDs in a conversation (filtered, paginated). @@ -77,9 +75,8 @@ message Message { message ConversationMetadata { string conversation_id = 1; - int64 version = 2; // CAS token (monotonic) - int64 message_count = 3; // count of active messages - google.protobuf.Timestamp updated_at = 4; + int64 message_count = 2; // count of active messages + google.protobuf.Timestamp updated_at = 3; } // Append @@ -91,8 +88,7 @@ message AppendMessageInput { message AppendConversationMessagesRequest { string conversation_id = 1; - int64 expect_version = 2; // optimistic concurrency; 0 to bypass on create - repeated AppendMessageInput messages = 3; + repeated AppendMessageInput messages = 2; } message AppendConversationMessagesResponse { @@ -106,9 +102,8 @@ message ListConversationMessagesRequest { repeated Role roles = 2; // filter roles (optional) repeated string kinds = 3; // filter kinds (optional) int64 seq_gte = 4; // inclusive lower bound (optional) - int64 seq_lte = 5; // inclusive upper bound (optional) - int32 page_size = 6; // server may cap - string page_token = 7; // opaque cursor + int32 page_size = 5; // server may cap + string page_token = 6; // opaque cursor } message ListConversationMessagesResponse { @@ -120,8 +115,7 @@ message ListConversationMessagesResponse { // Delete message DeleteConversationMessagesRequest { string conversation_id = 1; - int64 expect_version = 2; - repeated string message_ids = 3; + repeated string message_ids = 2; } message DeleteConversationMessagesResponse { @@ -138,9 +132,8 @@ message InsertConversationMessagesRequest { } string conversation_id = 1; int64 expect_version = 2; - int64 anchor_seq = 3; - Placement placement = 4; - repeated AppendMessageInput messages = 5; + Placement placement = 3; + repeated AppendMessageInput messages = 4; } message InsertConversationMessagesResponse { @@ -212,9 +205,8 @@ message ListSnapshotMessageIdsResponse { message ListConversationMessageIdsRequest { string conversation_id = 1; int64 seq_gte = 2; - int64 seq_lte = 3; - int32 page_size = 4; - string page_token = 5; + int32 page_size = 3; + string page_token = 4; } message ListConversationMessageIdsResponse { From b7c59c8fa239ed3766f6637f82dc17665d8b3e84 Mon Sep 17 00:00:00 2001 From: Casey Brooks Date: Fri, 27 Feb 2026 03:14:48 +0000 Subject: [PATCH 15/20] refactor(agent_state): remove seq filters --- proto/agynio/api/agent_state/v1/agent_state.proto | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/proto/agynio/api/agent_state/v1/agent_state.proto b/proto/agynio/api/agent_state/v1/agent_state.proto index eabf518..84b5b56 100644 --- a/proto/agynio/api/agent_state/v1/agent_state.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -101,9 +101,8 @@ message ListConversationMessagesRequest { string conversation_id = 1; repeated Role roles = 2; // filter roles (optional) repeated string kinds = 3; // filter kinds (optional) - int64 seq_gte = 4; // inclusive lower bound (optional) - int32 page_size = 5; // server may cap - string page_token = 6; // opaque cursor + int32 page_size = 4; // server may cap + string page_token = 5; // opaque cursor } message ListConversationMessagesResponse { @@ -204,9 +203,8 @@ message ListSnapshotMessageIdsResponse { message ListConversationMessageIdsRequest { string conversation_id = 1; - int64 seq_gte = 2; - int32 page_size = 3; - string page_token = 4; + int32 page_size = 2; + string page_token = 3; } message ListConversationMessageIdsResponse { From ecf416460ea1e9b6ccc8ab4a4f0b338a1116a320 Mon Sep 17 00:00:00 2001 From: Casey Brooks Date: Sat, 28 Feb 2026 01:32:24 +0000 Subject: [PATCH 16/20] feat(agent_state): add range ops and tool outputs --- .../api/agent_state/v1/agent_state.proto | 61 +++++++++++-------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/proto/agynio/api/agent_state/v1/agent_state.proto b/proto/agynio/api/agent_state/v1/agent_state.proto index 84b5b56..5406150 100644 --- a/proto/agynio/api/agent_state/v1/agent_state.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package agynio.api.agent_state.v1; +import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; option go_package = "github.com/agynio/api/gen/agynio/api/agent_state/v1;agentstatev1"; @@ -15,19 +16,19 @@ service AgentStateService { // Append messages (batch). rpc AppendConversationMessages(AppendConversationMessagesRequest) returns (AppendConversationMessagesResponse); - // List messages with filtering & pagination. + // List messages with filtering & pagination (conversation order: oldest to newest). rpc ListConversationMessages(ListConversationMessagesRequest) returns (ListConversationMessagesResponse); - // Delete messages by id. - rpc DeleteConversationMessages(DeleteConversationMessagesRequest) returns (DeleteConversationMessagesResponse); + // Replace a message range with references to existing messages. + rpc ReplaceConversationMessagesRange(ReplaceConversationMessagesRangeRequest) returns (ReplaceConversationMessagesRangeResponse); - // Insert messages using placement hints. - rpc InsertConversationMessages(InsertConversationMessagesRequest) returns (InsertConversationMessagesResponse); + // Delete a contiguous range of messages by id. + rpc DeleteConversationMessagesRange(DeleteConversationMessagesRangeRequest) returns (DeleteConversationMessagesRangeResponse); // Get conversation metadata (message counts and timestamps). rpc GetConversation(GetConversationRequest) returns (GetConversationResponse); - // List message IDs in a conversation (filtered, paginated). + // List message IDs in a conversation (filtered, paginated; oldest to newest). rpc ListConversationMessageIds(ListConversationMessageIdsRequest) returns (ListConversationMessageIdsResponse); // List messages in a snapshot (exact stored order, paginated). @@ -54,12 +55,22 @@ enum Role { // Text payload for now. Future extensions can add more oneof variants. message TextBody { string text = 1; } +message ToolOutputBody { + string tool_name = 1; + string tool_call_id = 2; + oneof content { + TextBody output_text = 3; + google.protobuf.Struct output_json = 4; + } +} + message MessageBody { oneof body { TextBody text = 1; + ToolOutputBody tool_output = 2; // Reserved for future extensions: - // ImageBody image = 2; - // AudioBody audio = 3; + // ImageBody image = 3; + // AudioBody audio = 4; } } @@ -96,7 +107,7 @@ message AppendConversationMessagesResponse { repeated Message appended = 2; } -// List +// List (results returned in conversation order: oldest to newest) message ListConversationMessagesRequest { string conversation_id = 1; repeated Role roles = 2; // filter roles (optional) @@ -111,33 +122,30 @@ message ListConversationMessagesResponse { string next_page_token = 3; } -// Delete -message DeleteConversationMessagesRequest { +// Replace +message ReplaceConversationMessagesRangeRequest { string conversation_id = 1; - repeated string message_ids = 2; + string from_message_id = 2; + string to_message_id = 3; + repeated string new_message_ids = 4; } -message DeleteConversationMessagesResponse { +message ReplaceConversationMessagesRangeResponse { ConversationMetadata metadata = 1; - repeated string deleted_message_ids = 2; + repeated string replaced_message_ids = 2; + repeated string inserted_message_ids = 3; } -// Insert -message InsertConversationMessagesRequest { - enum Placement { - PLACEMENT_UNSPECIFIED = 0; - PLACEMENT_BEFORE = 1; - PLACEMENT_AFTER = 2; - } +// Delete range +message DeleteConversationMessagesRangeRequest { string conversation_id = 1; - int64 expect_version = 2; - Placement placement = 3; - repeated AppendMessageInput messages = 4; + string from_message_id = 2; + string to_message_id = 3; } -message InsertConversationMessagesResponse { +message DeleteConversationMessagesRangeResponse { ConversationMetadata metadata = 1; - repeated Message inserted = 2; + repeated string deleted_message_ids = 2; } // Metadata @@ -201,6 +209,7 @@ message ListSnapshotMessageIdsResponse { string next_page_token = 2; } +// List conversation message IDs (results returned in conversation order: oldest to newest) message ListConversationMessageIdsRequest { string conversation_id = 1; int32 page_size = 2; From 6a345d4afc7be09f8760207fd21616df1cd05821 Mon Sep 17 00:00:00 2001 From: Casey Brooks Date: Sat, 28 Feb 2026 02:19:02 +0000 Subject: [PATCH 17/20] docs(agent_state): clarify range semantics --- proto/agynio/api/agent_state/v1/agent_state.proto | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/proto/agynio/api/agent_state/v1/agent_state.proto b/proto/agynio/api/agent_state/v1/agent_state.proto index 5406150..83291c3 100644 --- a/proto/agynio/api/agent_state/v1/agent_state.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -124,6 +124,11 @@ message ListConversationMessagesResponse { // Replace message ReplaceConversationMessagesRangeRequest { + // Semantics: + // - from_message_id and to_message_id set: replace inclusive range [from..to] with new_message_ids. + // - only from_message_id set: insert new_message_ids immediately after from_message_id (no deletions). + // - only to_message_id set: insert new_message_ids immediately before to_message_id (no deletions). + // - neither set: invalid; clients must use AppendConversationMessages. string conversation_id = 1; string from_message_id = 2; string to_message_id = 3; @@ -138,6 +143,11 @@ message ReplaceConversationMessagesRangeResponse { // Delete range message DeleteConversationMessagesRangeRequest { + // Semantics: + // - from_message_id and to_message_id set: delete inclusive range [from..to]. + // - only from_message_id set: delete from from_message_id through tail. + // - only to_message_id set: delete from head through to_message_id. + // - neither set: invalid. string conversation_id = 1; string from_message_id = 2; string to_message_id = 3; From 1c46ca36098814c8149c3bf2396979484307263f Mon Sep 17 00:00:00 2001 From: Casey Brooks Date: Sat, 28 Feb 2026 02:28:39 +0000 Subject: [PATCH 18/20] style(agent_state): apply buf formatting --- .../api/agent_state/v1/agent_state.proto | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/proto/agynio/api/agent_state/v1/agent_state.proto b/proto/agynio/api/agent_state/v1/agent_state.proto index 83291c3..0bcb969 100644 --- a/proto/agynio/api/agent_state/v1/agent_state.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -42,7 +42,6 @@ service AgentStateService { // a materialized copy of each message body in the provided order. // This guarantees reproducibility even after trims/edits. rpc CreateSnapshot(CreateSnapshotRequest) returns (CreateSnapshotResponse); - } enum Role { @@ -53,7 +52,9 @@ enum Role { } // Text payload for now. Future extensions can add more oneof variants. -message TextBody { string text = 1; } +message TextBody { + string text = 1; +} message ToolOutputBody { string tool_name = 1; @@ -76,17 +77,17 @@ message MessageBody { // Core message entity stored by APSS. message Message { - string id = 1; // server-assigned UUID - string conversation_id = 2; // routing key - Role role = 3; // user | assistant | tool - string kind = 4; // free-form tag (e.g., system, summary, memory, restriction, tool_output) - MessageBody body = 5; // payload + string id = 1; // server-assigned UUID + string conversation_id = 2; // routing key + Role role = 3; // user | assistant | tool + string kind = 4; // free-form tag (e.g., system, summary, memory, restriction, tool_output) + MessageBody body = 5; // payload google.protobuf.Timestamp created_at = 6; } message ConversationMetadata { string conversation_id = 1; - int64 message_count = 2; // count of active messages + int64 message_count = 2; // count of active messages google.protobuf.Timestamp updated_at = 3; } @@ -110,10 +111,10 @@ message AppendConversationMessagesResponse { // List (results returned in conversation order: oldest to newest) message ListConversationMessagesRequest { string conversation_id = 1; - repeated Role roles = 2; // filter roles (optional) - repeated string kinds = 3; // filter kinds (optional) - int32 page_size = 4; // server may cap - string page_token = 5; // opaque cursor + repeated Role roles = 2; // filter roles (optional) + repeated string kinds = 3; // filter kinds (optional) + int32 page_size = 4; // server may cap + string page_token = 5; // opaque cursor } message ListConversationMessagesResponse { @@ -159,8 +160,12 @@ message DeleteConversationMessagesRangeResponse { } // Metadata -message GetConversationRequest { string conversation_id = 1; } -message GetConversationResponse { ConversationMetadata metadata = 1; } +message GetConversationRequest { + string conversation_id = 1; +} +message GetConversationResponse { + ConversationMetadata metadata = 1; +} // Create a materialized context snapshot for a specific LLM call. // The client supplies the exact message IDs; APSS stores @@ -173,23 +178,23 @@ message GetConversationResponse { ConversationMetadata metadata = 1; } // A materialized snapshot of the exact context sent to an LLM call. message ContextSnapshot { - string snapshot_id = 1; // server-assigned UUID - string conversation_id = 2; // conversation this snapshot belongs to + string snapshot_id = 1; // server-assigned UUID + string conversation_id = 2; // conversation this snapshot belongs to google.protobuf.Timestamp created_at = 4; } // Each snapshot item is a materialized message copy with integrity hash. message SnapshotItem { - string message_id = 1; // original message id - Role role = 2; // original role - string kind = 3; // original kind - MessageBody body = 4; // materialized body copy - string body_sha256 = 5; // integrity hash of the serialized body + string message_id = 1; // original message id + Role role = 2; // original role + string kind = 3; // original kind + MessageBody body = 4; // materialized body copy + string body_sha256 = 5; // integrity hash of the serialized body } message CreateSnapshotRequest { string conversation_id = 1; - repeated string message_ids = 2; // exact order to persist in the snapshot + repeated string message_ids = 2; // exact order to persist in the snapshot } message CreateSnapshotResponse { From 662ef50a7eb7a03c773adcf6105fd2e9ec8f04b5 Mon Sep 17 00:00:00 2001 From: Casey Brooks Date: Sat, 28 Feb 2026 02:53:51 +0000 Subject: [PATCH 19/20] feat(agent_state): simplify tool outputs --- .../api/agent_state/v1/agent_state.proto | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/proto/agynio/api/agent_state/v1/agent_state.proto b/proto/agynio/api/agent_state/v1/agent_state.proto index 0bcb969..6c931a5 100644 --- a/proto/agynio/api/agent_state/v1/agent_state.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -2,14 +2,13 @@ syntax = "proto3"; package agynio.api.agent_state.v1; -import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; option go_package = "github.com/agynio/api/gen/agynio/api/agent_state/v1;agentstatev1"; // Agent Persistent State Service (APSS) // - conversationId-only API (threadId/nodeId mapping handled outside) -// - message bodies are well-typed; text-only now, extensible via oneof +// - message bodies are well-typed (text and tool outputs via oneof) // - no external type references service AgentStateService { @@ -56,22 +55,46 @@ message TextBody { string text = 1; } +message ToolTextOutput { + string text = 1; +} + +enum ImageDetail { + IMAGE_DETAIL_UNSPECIFIED = 0; + IMAGE_DETAIL_AUTO = 1; + IMAGE_DETAIL_LOW = 2; + IMAGE_DETAIL_HIGH = 3; +} + +message ToolImageOutput { + oneof source { + string openai_file_id = 1; + string blob_id = 2; + string external_url = 3; + string data_url = 4; + bytes data = 5; + } + string mime_type = 10; + ImageDetail detail = 11; + int32 width_px = 12; + int32 height_px = 13; + string sha256 = 14; +} + message ToolOutputBody { string tool_name = 1; string tool_call_id = 2; - oneof content { - TextBody output_text = 3; - google.protobuf.Struct output_json = 4; + oneof output { + ToolTextOutput text = 3; + ToolImageOutput image = 4; } + map metadata = 10; // optional } message MessageBody { oneof body { TextBody text = 1; ToolOutputBody tool_output = 2; - // Reserved for future extensions: - // ImageBody image = 3; - // AudioBody audio = 4; } } From e2bd91430395c9b38fc2627432dd7cdc3fa5fff2 Mon Sep 17 00:00:00 2001 From: Casey Brooks Date: Sat, 28 Feb 2026 03:46:03 +0000 Subject: [PATCH 20/20] refactor(agent_state): limit image output source --- proto/agynio/api/agent_state/v1/agent_state.proto | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/proto/agynio/api/agent_state/v1/agent_state.proto b/proto/agynio/api/agent_state/v1/agent_state.proto index 6c931a5..43344ca 100644 --- a/proto/agynio/api/agent_state/v1/agent_state.proto +++ b/proto/agynio/api/agent_state/v1/agent_state.proto @@ -67,13 +67,7 @@ enum ImageDetail { } message ToolImageOutput { - oneof source { - string openai_file_id = 1; - string blob_id = 2; - string external_url = 3; - string data_url = 4; - bytes data = 5; - } + string external_url = 1; string mime_type = 10; ImageDetail detail = 11; int32 width_px = 12;