diff --git a/.changeset/agent-thread-content.md b/.changeset/agent-thread-content.md new file mode 100644 index 000000000..c92dca73c --- /dev/null +++ b/.changeset/agent-thread-content.md @@ -0,0 +1,6 @@ +--- +"@livekit/protocol": minor +"github.com/livekit/protocol": minor +--- + +AgentThread history and tasks messages; shared conversation schema (thread_schema_v1.sql) diff --git a/livekit/agent/livekit_agent_thread.pb.go b/livekit/agent/livekit_agent_thread.pb.go index 6d799107a..ceaa0bac5 100644 --- a/livekit/agent/livekit_agent_thread.pb.go +++ b/livekit/agent/livekit_agent_thread.pb.go @@ -16,7 +16,8 @@ // channels and over time. Identified by an "AT_" id. Holds a scope, attributes, // an idle TTL and the AgentDB database created with it. An internal key (a // LiveKit phone number) can find it again. Served by cloud-protocol's -// AgentThreads. +// AgentThreads. The conversation inside follows thread_schema_v1.sql, shared +// by the agents framework, which writes it, and the platform, which reads it. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: @@ -31,6 +32,7 @@ import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb "google.golang.org/protobuf/types/known/durationpb" + structpb "google.golang.org/protobuf/types/known/structpb" timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" @@ -544,6 +546,426 @@ func (*AgentThread_DeleteResponse) Descriptor() ([]byte, []int) { return file_agent_livekit_agent_thread_proto_rawDescGZIP(), []int{0, 7} } +// The conversation, oldest first. +type AgentThread_HistoryRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ThreadId string `protobuf:"bytes,1,opt,name=thread_id,json=threadId,proto3" json:"thread_id,omitempty"` + // One agent's history; empty for every agent's. + Agent string `protobuf:"bytes,2,opt,name=agent,proto3" json:"agent,omitempty"` + // Cursor from the previous HistoryResponse; empty for the first page. + After string `protobuf:"bytes,3,opt,name=after,proto3" json:"after,omitempty"` + Limit int32 `protobuf:"varint,4,opt,name=limit,proto3" json:"limit,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AgentThread_HistoryRequest) Reset() { + *x = AgentThread_HistoryRequest{} + mi := &file_agent_livekit_agent_thread_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AgentThread_HistoryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentThread_HistoryRequest) ProtoMessage() {} + +func (x *AgentThread_HistoryRequest) ProtoReflect() protoreflect.Message { + mi := &file_agent_livekit_agent_thread_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AgentThread_HistoryRequest.ProtoReflect.Descriptor instead. +func (*AgentThread_HistoryRequest) Descriptor() ([]byte, []int) { + return file_agent_livekit_agent_thread_proto_rawDescGZIP(), []int{0, 8} +} + +func (x *AgentThread_HistoryRequest) GetThreadId() string { + if x != nil { + return x.ThreadId + } + return "" +} + +func (x *AgentThread_HistoryRequest) GetAgent() string { + if x != nil { + return x.Agent + } + return "" +} + +func (x *AgentThread_HistoryRequest) GetAfter() string { + if x != nil { + return x.After + } + return "" +} + +func (x *AgentThread_HistoryRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +type AgentThread_HistoryResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Items []*AgentThread_Item `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` + // Empty at the end. + After string `protobuf:"bytes,2,opt,name=after,proto3" json:"after,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AgentThread_HistoryResponse) Reset() { + *x = AgentThread_HistoryResponse{} + mi := &file_agent_livekit_agent_thread_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AgentThread_HistoryResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentThread_HistoryResponse) ProtoMessage() {} + +func (x *AgentThread_HistoryResponse) ProtoReflect() protoreflect.Message { + mi := &file_agent_livekit_agent_thread_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AgentThread_HistoryResponse.ProtoReflect.Descriptor instead. +func (*AgentThread_HistoryResponse) Descriptor() ([]byte, []int) { + return file_agent_livekit_agent_thread_proto_rawDescGZIP(), []int{0, 9} +} + +func (x *AgentThread_HistoryResponse) GetItems() []*AgentThread_Item { + if x != nil { + return x.Items + } + return nil +} + +func (x *AgentThread_HistoryResponse) GetAfter() string { + if x != nil { + return x.After + } + return "" +} + +// One chat item of an agent's history. Stored as the JSON of +// ChatContext.ChatItem (items.payload in thread_schema_v1.sql). +type AgentThread_Item struct { + state protoimpl.MessageState `protogen:"open.v1"` + Agent string `protobuf:"bytes,1,opt,name=agent,proto3" json:"agent,omitempty"` + // Position in the agent's history. + Seq int64 `protobuf:"varint,2,opt,name=seq,proto3" json:"seq,omitempty"` + // The A2A message the item came from. + MessageId string `protobuf:"bytes,3,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` + Item *ChatContext_ChatItem `protobuf:"bytes,4,opt,name=item,proto3" json:"item,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AgentThread_Item) Reset() { + *x = AgentThread_Item{} + mi := &file_agent_livekit_agent_thread_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AgentThread_Item) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentThread_Item) ProtoMessage() {} + +func (x *AgentThread_Item) ProtoReflect() protoreflect.Message { + mi := &file_agent_livekit_agent_thread_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AgentThread_Item.ProtoReflect.Descriptor instead. +func (*AgentThread_Item) Descriptor() ([]byte, []int) { + return file_agent_livekit_agent_thread_proto_rawDescGZIP(), []int{0, 10} +} + +func (x *AgentThread_Item) GetAgent() string { + if x != nil { + return x.Agent + } + return "" +} + +func (x *AgentThread_Item) GetSeq() int64 { + if x != nil { + return x.Seq + } + return 0 +} + +func (x *AgentThread_Item) GetMessageId() string { + if x != nil { + return x.MessageId + } + return "" +} + +func (x *AgentThread_Item) GetItem() *ChatContext_ChatItem { + if x != nil { + return x.Item + } + return nil +} + +// The thread's A2A tasks, oldest first. +type AgentThread_ListTasksRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ThreadId string `protobuf:"bytes,1,opt,name=thread_id,json=threadId,proto3" json:"thread_id,omitempty"` + Agent string `protobuf:"bytes,2,opt,name=agent,proto3" json:"agent,omitempty"` + After string `protobuf:"bytes,3,opt,name=after,proto3" json:"after,omitempty"` + Limit int32 `protobuf:"varint,4,opt,name=limit,proto3" json:"limit,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AgentThread_ListTasksRequest) Reset() { + *x = AgentThread_ListTasksRequest{} + mi := &file_agent_livekit_agent_thread_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AgentThread_ListTasksRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentThread_ListTasksRequest) ProtoMessage() {} + +func (x *AgentThread_ListTasksRequest) ProtoReflect() protoreflect.Message { + mi := &file_agent_livekit_agent_thread_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AgentThread_ListTasksRequest.ProtoReflect.Descriptor instead. +func (*AgentThread_ListTasksRequest) Descriptor() ([]byte, []int) { + return file_agent_livekit_agent_thread_proto_rawDescGZIP(), []int{0, 11} +} + +func (x *AgentThread_ListTasksRequest) GetThreadId() string { + if x != nil { + return x.ThreadId + } + return "" +} + +func (x *AgentThread_ListTasksRequest) GetAgent() string { + if x != nil { + return x.Agent + } + return "" +} + +func (x *AgentThread_ListTasksRequest) GetAfter() string { + if x != nil { + return x.After + } + return "" +} + +func (x *AgentThread_ListTasksRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +type AgentThread_ListTasksResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Tasks []*AgentThread_Task `protobuf:"bytes,1,rep,name=tasks,proto3" json:"tasks,omitempty"` + After string `protobuf:"bytes,2,opt,name=after,proto3" json:"after,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AgentThread_ListTasksResponse) Reset() { + *x = AgentThread_ListTasksResponse{} + mi := &file_agent_livekit_agent_thread_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AgentThread_ListTasksResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentThread_ListTasksResponse) ProtoMessage() {} + +func (x *AgentThread_ListTasksResponse) ProtoReflect() protoreflect.Message { + mi := &file_agent_livekit_agent_thread_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AgentThread_ListTasksResponse.ProtoReflect.Descriptor instead. +func (*AgentThread_ListTasksResponse) Descriptor() ([]byte, []int) { + return file_agent_livekit_agent_thread_proto_rawDescGZIP(), []int{0, 12} +} + +func (x *AgentThread_ListTasksResponse) GetTasks() []*AgentThread_Task { + if x != nil { + return x.Tasks + } + return nil +} + +func (x *AgentThread_ListTasksResponse) GetAfter() string { + if x != nil { + return x.After + } + return "" +} + +type AgentThread_Task struct { + state protoimpl.MessageState `protogen:"open.v1"` + TaskId string `protobuf:"bytes,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` + Agent string `protobuf:"bytes,2,opt,name=agent,proto3" json:"agent,omitempty"` + // "submitted", "working", "input-required", "completed", "failed", + // "canceled" or "rejected". + Status string `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` + StatusMessage *structpb.Struct `protobuf:"bytes,4,opt,name=status_message,json=statusMessage,proto3" json:"status_message,omitempty"` + History []*ChatContext_ChatItem `protobuf:"bytes,5,rep,name=history,proto3" json:"history,omitempty"` + Artifacts *structpb.ListValue `protobuf:"bytes,6,opt,name=artifacts,proto3" json:"artifacts,omitempty"` + // The task that submitted this one, if any. + Parent string `protobuf:"bytes,7,opt,name=parent,proto3" json:"parent,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AgentThread_Task) Reset() { + *x = AgentThread_Task{} + mi := &file_agent_livekit_agent_thread_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AgentThread_Task) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentThread_Task) ProtoMessage() {} + +func (x *AgentThread_Task) ProtoReflect() protoreflect.Message { + mi := &file_agent_livekit_agent_thread_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AgentThread_Task.ProtoReflect.Descriptor instead. +func (*AgentThread_Task) Descriptor() ([]byte, []int) { + return file_agent_livekit_agent_thread_proto_rawDescGZIP(), []int{0, 13} +} + +func (x *AgentThread_Task) GetTaskId() string { + if x != nil { + return x.TaskId + } + return "" +} + +func (x *AgentThread_Task) GetAgent() string { + if x != nil { + return x.Agent + } + return "" +} + +func (x *AgentThread_Task) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *AgentThread_Task) GetStatusMessage() *structpb.Struct { + if x != nil { + return x.StatusMessage + } + return nil +} + +func (x *AgentThread_Task) GetHistory() []*ChatContext_ChatItem { + if x != nil { + return x.History + } + return nil +} + +func (x *AgentThread_Task) GetArtifacts() *structpb.ListValue { + if x != nil { + return x.Artifacts + } + return nil +} + +func (x *AgentThread_Task) GetParent() string { + if x != nil { + return x.Parent + } + return "" +} + +func (x *AgentThread_Task) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + type AgentThread_Thread struct { state protoimpl.MessageState `protogen:"open.v1"` ThreadId string `protobuf:"bytes,1,opt,name=thread_id,json=threadId,proto3" json:"thread_id,omitempty"` @@ -565,7 +987,7 @@ type AgentThread_Thread struct { func (x *AgentThread_Thread) Reset() { *x = AgentThread_Thread{} - mi := &file_agent_livekit_agent_thread_proto_msgTypes[9] + mi := &file_agent_livekit_agent_thread_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -577,7 +999,7 @@ func (x *AgentThread_Thread) String() string { func (*AgentThread_Thread) ProtoMessage() {} func (x *AgentThread_Thread) ProtoReflect() protoreflect.Message { - mi := &file_agent_livekit_agent_thread_proto_msgTypes[9] + mi := &file_agent_livekit_agent_thread_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -590,7 +1012,7 @@ func (x *AgentThread_Thread) ProtoReflect() protoreflect.Message { // Deprecated: Use AgentThread_Thread.ProtoReflect.Descriptor instead. func (*AgentThread_Thread) Descriptor() ([]byte, []int) { - return file_agent_livekit_agent_thread_proto_rawDescGZIP(), []int{0, 8} + return file_agent_livekit_agent_thread_proto_rawDescGZIP(), []int{0, 14} } func (x *AgentThread_Thread) GetThreadId() string { @@ -660,7 +1082,7 @@ var File_agent_livekit_agent_thread_proto protoreflect.FileDescriptor const file_agent_livekit_agent_thread_proto_rawDesc = "" + "\n" + - " agent/livekit_agent_thread.proto\x12\rlivekit.agent\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x14logger/options.proto\"\xfc\v\n" + + " agent/livekit_agent_thread.proto\x12\rlivekit.agent\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a!agent/livekit_agent_session.proto\x1a\x14logger/options.proto\"\xb5\x13\n" + "\vAgentThread\x1a\x90\x02\n" + "\rCreateRequest\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x19\n" + @@ -698,7 +1120,39 @@ const file_agent_livekit_agent_thread_proto_rawDesc = "" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a9\n" + "\rDeleteRequest\x12(\n" + "\tthread_id\x18\x01 \x01(\tB\v\xbaP\bthreadIDR\bthreadId\x1a\x10\n" + - "\x0eDeleteResponse\x1a\x80\x04\n" + + "\x0eDeleteResponse\x1a|\n" + + "\x0eHistoryRequest\x12(\n" + + "\tthread_id\x18\x01 \x01(\tB\v\xbaP\bthreadIDR\bthreadId\x12\x14\n" + + "\x05agent\x18\x02 \x01(\tR\x05agent\x12\x14\n" + + "\x05after\x18\x03 \x01(\tR\x05after\x12\x14\n" + + "\x05limit\x18\x04 \x01(\x05R\x05limit\x1a^\n" + + "\x0fHistoryResponse\x125\n" + + "\x05items\x18\x01 \x03(\v2\x1f.livekit.agent.AgentThread.ItemR\x05items\x12\x14\n" + + "\x05after\x18\x02 \x01(\tR\x05after\x1a\x8b\x01\n" + + "\x04Item\x12\x14\n" + + "\x05agent\x18\x01 \x01(\tR\x05agent\x12\x10\n" + + "\x03seq\x18\x02 \x01(\x03R\x03seq\x12\x1d\n" + + "\n" + + "message_id\x18\x03 \x01(\tR\tmessageId\x12<\n" + + "\x04item\x18\x04 \x01(\v2#.livekit.agent.ChatContext.ChatItemB\x03\xc0P\x01R\x04item\x1a~\n" + + "\x10ListTasksRequest\x12(\n" + + "\tthread_id\x18\x01 \x01(\tB\v\xbaP\bthreadIDR\bthreadId\x12\x14\n" + + "\x05agent\x18\x02 \x01(\tR\x05agent\x12\x14\n" + + "\x05after\x18\x03 \x01(\tR\x05after\x12\x14\n" + + "\x05limit\x18\x04 \x01(\x05R\x05limit\x1a`\n" + + "\x11ListTasksResponse\x125\n" + + "\x05tasks\x18\x01 \x03(\v2\x1f.livekit.agent.AgentThread.TaskR\x05tasks\x12\x14\n" + + "\x05after\x18\x02 \x01(\tR\x05after\x1a\xe8\x02\n" + + "\x04Task\x12\x17\n" + + "\atask_id\x18\x01 \x01(\tR\x06taskId\x12\x14\n" + + "\x05agent\x18\x02 \x01(\tR\x05agent\x12\x16\n" + + "\x06status\x18\x03 \x01(\tR\x06status\x12C\n" + + "\x0estatus_message\x18\x04 \x01(\v2\x17.google.protobuf.StructB\x03\xc0P\x01R\rstatusMessage\x12B\n" + + "\ahistory\x18\x05 \x03(\v2#.livekit.agent.ChatContext.ChatItemB\x03\xc0P\x01R\ahistory\x12=\n" + + "\tartifacts\x18\x06 \x01(\v2\x1a.google.protobuf.ListValueB\x03\xc0P\x01R\tartifacts\x12\x16\n" + + "\x06parent\x18\a \x01(\tR\x06parent\x129\n" + + "\n" + + "updated_at\x18\b \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\x1a\x80\x04\n" + "\x06Thread\x12(\n" + "\tthread_id\x18\x01 \x01(\tB\v\xbaP\bthreadIDR\bthreadId\x12\x10\n" + "\x03key\x18\x02 \x01(\tR\x03key\x12\x19\n" + @@ -731,41 +1185,57 @@ func file_agent_livekit_agent_thread_proto_rawDescGZIP() []byte { return file_agent_livekit_agent_thread_proto_rawDescData } -var file_agent_livekit_agent_thread_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_agent_livekit_agent_thread_proto_msgTypes = make([]protoimpl.MessageInfo, 19) var file_agent_livekit_agent_thread_proto_goTypes = []any{ - (*AgentThread)(nil), // 0: livekit.agent.AgentThread - (*AgentThread_CreateRequest)(nil), // 1: livekit.agent.AgentThread.CreateRequest - (*AgentThread_CreateResponse)(nil), // 2: livekit.agent.AgentThread.CreateResponse - (*AgentThread_GetRequest)(nil), // 3: livekit.agent.AgentThread.GetRequest - (*AgentThread_ListRequest)(nil), // 4: livekit.agent.AgentThread.ListRequest - (*AgentThread_ListResponse)(nil), // 5: livekit.agent.AgentThread.ListResponse - (*AgentThread_UpdateRequest)(nil), // 6: livekit.agent.AgentThread.UpdateRequest - (*AgentThread_DeleteRequest)(nil), // 7: livekit.agent.AgentThread.DeleteRequest - (*AgentThread_DeleteResponse)(nil), // 8: livekit.agent.AgentThread.DeleteResponse - (*AgentThread_Thread)(nil), // 9: livekit.agent.AgentThread.Thread - nil, // 10: livekit.agent.AgentThread.CreateRequest.AttributesEntry - nil, // 11: livekit.agent.AgentThread.UpdateRequest.AttributesEntry - nil, // 12: livekit.agent.AgentThread.Thread.AttributesEntry - (*durationpb.Duration)(nil), // 13: google.protobuf.Duration - (*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp + (*AgentThread)(nil), // 0: livekit.agent.AgentThread + (*AgentThread_CreateRequest)(nil), // 1: livekit.agent.AgentThread.CreateRequest + (*AgentThread_CreateResponse)(nil), // 2: livekit.agent.AgentThread.CreateResponse + (*AgentThread_GetRequest)(nil), // 3: livekit.agent.AgentThread.GetRequest + (*AgentThread_ListRequest)(nil), // 4: livekit.agent.AgentThread.ListRequest + (*AgentThread_ListResponse)(nil), // 5: livekit.agent.AgentThread.ListResponse + (*AgentThread_UpdateRequest)(nil), // 6: livekit.agent.AgentThread.UpdateRequest + (*AgentThread_DeleteRequest)(nil), // 7: livekit.agent.AgentThread.DeleteRequest + (*AgentThread_DeleteResponse)(nil), // 8: livekit.agent.AgentThread.DeleteResponse + (*AgentThread_HistoryRequest)(nil), // 9: livekit.agent.AgentThread.HistoryRequest + (*AgentThread_HistoryResponse)(nil), // 10: livekit.agent.AgentThread.HistoryResponse + (*AgentThread_Item)(nil), // 11: livekit.agent.AgentThread.Item + (*AgentThread_ListTasksRequest)(nil), // 12: livekit.agent.AgentThread.ListTasksRequest + (*AgentThread_ListTasksResponse)(nil), // 13: livekit.agent.AgentThread.ListTasksResponse + (*AgentThread_Task)(nil), // 14: livekit.agent.AgentThread.Task + (*AgentThread_Thread)(nil), // 15: livekit.agent.AgentThread.Thread + nil, // 16: livekit.agent.AgentThread.CreateRequest.AttributesEntry + nil, // 17: livekit.agent.AgentThread.UpdateRequest.AttributesEntry + nil, // 18: livekit.agent.AgentThread.Thread.AttributesEntry + (*durationpb.Duration)(nil), // 19: google.protobuf.Duration + (*ChatContext_ChatItem)(nil), // 20: livekit.agent.ChatContext.ChatItem + (*structpb.Struct)(nil), // 21: google.protobuf.Struct + (*structpb.ListValue)(nil), // 22: google.protobuf.ListValue + (*timestamppb.Timestamp)(nil), // 23: google.protobuf.Timestamp } var file_agent_livekit_agent_thread_proto_depIdxs = []int32{ - 10, // 0: livekit.agent.AgentThread.CreateRequest.attributes:type_name -> livekit.agent.AgentThread.CreateRequest.AttributesEntry - 13, // 1: livekit.agent.AgentThread.CreateRequest.idle_ttl:type_name -> google.protobuf.Duration - 9, // 2: livekit.agent.AgentThread.CreateResponse.thread:type_name -> livekit.agent.AgentThread.Thread - 9, // 3: livekit.agent.AgentThread.ListResponse.threads:type_name -> livekit.agent.AgentThread.Thread - 11, // 4: livekit.agent.AgentThread.UpdateRequest.attributes:type_name -> livekit.agent.AgentThread.UpdateRequest.AttributesEntry - 13, // 5: livekit.agent.AgentThread.UpdateRequest.idle_ttl:type_name -> google.protobuf.Duration - 12, // 6: livekit.agent.AgentThread.Thread.attributes:type_name -> livekit.agent.AgentThread.Thread.AttributesEntry - 14, // 7: livekit.agent.AgentThread.Thread.created_at:type_name -> google.protobuf.Timestamp - 14, // 8: livekit.agent.AgentThread.Thread.last_active:type_name -> google.protobuf.Timestamp - 14, // 9: livekit.agent.AgentThread.Thread.expires_at:type_name -> google.protobuf.Timestamp - 13, // 10: livekit.agent.AgentThread.Thread.idle_ttl:type_name -> google.protobuf.Duration - 11, // [11:11] is the sub-list for method output_type - 11, // [11:11] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name + 16, // 0: livekit.agent.AgentThread.CreateRequest.attributes:type_name -> livekit.agent.AgentThread.CreateRequest.AttributesEntry + 19, // 1: livekit.agent.AgentThread.CreateRequest.idle_ttl:type_name -> google.protobuf.Duration + 15, // 2: livekit.agent.AgentThread.CreateResponse.thread:type_name -> livekit.agent.AgentThread.Thread + 15, // 3: livekit.agent.AgentThread.ListResponse.threads:type_name -> livekit.agent.AgentThread.Thread + 17, // 4: livekit.agent.AgentThread.UpdateRequest.attributes:type_name -> livekit.agent.AgentThread.UpdateRequest.AttributesEntry + 19, // 5: livekit.agent.AgentThread.UpdateRequest.idle_ttl:type_name -> google.protobuf.Duration + 11, // 6: livekit.agent.AgentThread.HistoryResponse.items:type_name -> livekit.agent.AgentThread.Item + 20, // 7: livekit.agent.AgentThread.Item.item:type_name -> livekit.agent.ChatContext.ChatItem + 14, // 8: livekit.agent.AgentThread.ListTasksResponse.tasks:type_name -> livekit.agent.AgentThread.Task + 21, // 9: livekit.agent.AgentThread.Task.status_message:type_name -> google.protobuf.Struct + 20, // 10: livekit.agent.AgentThread.Task.history:type_name -> livekit.agent.ChatContext.ChatItem + 22, // 11: livekit.agent.AgentThread.Task.artifacts:type_name -> google.protobuf.ListValue + 23, // 12: livekit.agent.AgentThread.Task.updated_at:type_name -> google.protobuf.Timestamp + 18, // 13: livekit.agent.AgentThread.Thread.attributes:type_name -> livekit.agent.AgentThread.Thread.AttributesEntry + 23, // 14: livekit.agent.AgentThread.Thread.created_at:type_name -> google.protobuf.Timestamp + 23, // 15: livekit.agent.AgentThread.Thread.last_active:type_name -> google.protobuf.Timestamp + 23, // 16: livekit.agent.AgentThread.Thread.expires_at:type_name -> google.protobuf.Timestamp + 19, // 17: livekit.agent.AgentThread.Thread.idle_ttl:type_name -> google.protobuf.Duration + 18, // [18:18] is the sub-list for method output_type + 18, // [18:18] is the sub-list for method input_type + 18, // [18:18] is the sub-list for extension type_name + 18, // [18:18] is the sub-list for extension extendee + 0, // [0:18] is the sub-list for field type_name } func init() { file_agent_livekit_agent_thread_proto_init() } @@ -773,6 +1243,7 @@ func file_agent_livekit_agent_thread_proto_init() { if File_agent_livekit_agent_thread_proto != nil { return } + file_agent_livekit_agent_session_proto_init() file_agent_livekit_agent_thread_proto_msgTypes[3].OneofWrappers = []any{ (*AgentThread_GetRequest_ThreadId)(nil), (*AgentThread_GetRequest_Key)(nil), @@ -783,7 +1254,7 @@ func file_agent_livekit_agent_thread_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_agent_livekit_agent_thread_proto_rawDesc), len(file_agent_livekit_agent_thread_proto_rawDesc)), NumEnums: 0, - NumMessages: 13, + NumMessages: 19, NumExtensions: 0, NumServices: 0, }, diff --git a/livekit/agent/thread_schema.go b/livekit/agent/thread_schema.go new file mode 100644 index 000000000..fe2c9a8a9 --- /dev/null +++ b/livekit/agent/thread_schema.go @@ -0,0 +1,27 @@ +// Copyright 2026 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package agent + +import _ "embed" + +// ThreadSchemaVersion is the newest conversation schema a thread's database +// can hold. +const ThreadSchemaVersion = 1 + +// ThreadSchemaV1 creates version 1 of the conversation schema, one statement +// per ";" (comments hold none). +// +//go:embed thread_schema_v1.sql +var ThreadSchemaV1 string diff --git a/livekit/agent/thread_schema_v1.sql b/livekit/agent/thread_schema_v1.sql new file mode 100644 index 000000000..507a0c7f6 --- /dev/null +++ b/livekit/agent/thread_schema_v1.sql @@ -0,0 +1,78 @@ +-- Tables inside a thread's database, created by the agents framework. The +-- platform reads items and tasks. items.payload and tasks.history hold +-- livekit.agent.ChatContext.ChatItem as protobuf JSON. + +CREATE TABLE schema_version ( + version INTEGER NOT NULL, + applied_at INTEGER NOT NULL +); + +CREATE TABLE items ( + lane TEXT NOT NULL, + id TEXT PRIMARY KEY, + seq INTEGER NOT NULL, + type TEXT NOT NULL, + message_id TEXT, + payload JSONB NOT NULL +); +CREATE INDEX items_lane_seq ON items (lane, seq); +CREATE UNIQUE INDEX items_lane_message ON items (lane, message_id) WHERE message_id IS NOT NULL; + +CREATE TABLE tasks ( + task_id TEXT PRIMARY KEY, + lane TEXT NOT NULL, + seq INTEGER NOT NULL, + status TEXT NOT NULL, + message_id TEXT, + task JSONB NOT NULL, + agent TEXT NOT NULL, + due_at INTEGER, + deadline INTEGER, + ask JSONB, + status_message JSONB, + history JSONB NOT NULL, + artifacts JSONB, + parent TEXT, + push_configs JSONB, + attempts INTEGER NOT NULL DEFAULT 0, + updated_at INTEGER NOT NULL +); +CREATE INDEX tasks_lane_status_due ON tasks (lane, status, due_at); +CREATE UNIQUE INDEX tasks_lane_message ON tasks (lane, message_id) WHERE message_id IS NOT NULL; + +-- One row per agent. +CREATE TABLE lanes ( + lane TEXT PRIMARY KEY, + version INTEGER NOT NULL, + state JSONB, + agent_stack JSONB, + lease_holder TEXT, + lease_token TEXT, + lease_until INTEGER NOT NULL DEFAULT 0, + waiter TEXT, + drained_seq INTEGER NOT NULL DEFAULT 0 +); + +-- The tables below are private to the framework. + +CREATE TABLE events ( + seq INTEGER PRIMARY KEY, + ts_ms INTEGER NOT NULL, + lane TEXT NOT NULL, + run_id TEXT NOT NULL, + author TEXT NOT NULL, + type TEXT NOT NULL, + payload JSONB NOT NULL +); +CREATE INDEX events_lane_seq ON events (lane, seq); + +CREATE TABLE inbox ( + seq INTEGER PRIMARY KEY, + lane TEXT NOT NULL, + task_id TEXT NOT NULL, + message_id TEXT NOT NULL, + message JSONB NOT NULL, + received_at INTEGER NOT NULL +); +CREATE INDEX inbox_lane_seq ON inbox (lane, seq); +CREATE UNIQUE INDEX inbox_lane_message ON inbox (lane, message_id); diff --git a/protobufs/agent/livekit_agent_thread.proto b/protobufs/agent/livekit_agent_thread.proto index a0edf0d9a..072eece26 100644 --- a/protobufs/agent/livekit_agent_thread.proto +++ b/protobufs/agent/livekit_agent_thread.proto @@ -16,7 +16,8 @@ // channels and over time. Identified by an "AT_" id. Holds a scope, attributes, // an idle TTL and the AgentDB database created with it. An internal key (a // LiveKit phone number) can find it again. Served by cloud-protocol's -// AgentThreads. +// AgentThreads. The conversation inside follows thread_schema_v1.sql, shared +// by the agents framework, which writes it, and the platform, which reads it. syntax = "proto3"; @@ -25,7 +26,9 @@ package livekit.agent; option go_package = "github.com/livekit/protocol/livekit/agent"; import "google/protobuf/duration.proto"; +import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; +import "agent/livekit_agent_session.proto"; import "logger/options.proto"; message AgentThread { @@ -82,6 +85,60 @@ message AgentThread { message DeleteResponse {} + // The conversation, oldest first. + message HistoryRequest { + string thread_id = 1 [(logger.name) = "threadID"]; + // One agent's history; empty for every agent's. + string agent = 2; + // Cursor from the previous HistoryResponse; empty for the first page. + string after = 3; + int32 limit = 4; + } + + message HistoryResponse { + repeated Item items = 1; + // Empty at the end. + string after = 2; + } + + // One chat item of an agent's history. Stored as the JSON of + // ChatContext.ChatItem (items.payload in thread_schema_v1.sql). + message Item { + string agent = 1; + // Position in the agent's history. + int64 seq = 2; + // The A2A message the item came from. + string message_id = 3; + ChatContext.ChatItem item = 4 [(logger.sensitivity) = SENSITIVITY_PII]; + } + + // The thread's A2A tasks, oldest first. + message ListTasksRequest { + string thread_id = 1 [(logger.name) = "threadID"]; + string agent = 2; + string after = 3; + int32 limit = 4; + } + + message ListTasksResponse { + repeated Task tasks = 1; + string after = 2; + } + + message Task { + string task_id = 1; + string agent = 2; + // "submitted", "working", "input-required", "completed", "failed", + // "canceled" or "rejected". + string status = 3; + google.protobuf.Struct status_message = 4 [(logger.sensitivity) = SENSITIVITY_PII]; + repeated ChatContext.ChatItem history = 5 [(logger.sensitivity) = SENSITIVITY_PII]; + google.protobuf.ListValue artifacts = 6 [(logger.sensitivity) = SENSITIVITY_PII]; + // The task that submitted this one, if any. + string parent = 7; + google.protobuf.Timestamp updated_at = 8; + } + message Thread { string thread_id = 1 [(logger.name) = "threadID"]; // Internal: the key the thread was created with, if any.