diff --git a/src/grpc/buf.gen.yaml b/src/grpc/buf.gen.yaml index 6ca9834e..8f6cd84f 100644 --- a/src/grpc/buf.gen.yaml +++ b/src/grpc/buf.gen.yaml @@ -11,7 +11,7 @@ inputs: - google/rpc/status.proto - google/rpc/error_details.proto plugins: - - remote: buf.build/community/stephenh-ts-proto:v2.6.1 + - remote: buf.build/community/stephenh-ts-proto:v2.11.0 opt: - oneof=unions-value - importSuffix=.js @@ -21,4 +21,4 @@ plugins: - outputJsonMethods=false - outputPartialMethods=false - useDate=string - out: ./pb \ No newline at end of file + out: ./pb diff --git a/src/grpc/pb/google/protobuf/struct.ts b/src/grpc/pb/google/protobuf/struct.ts index ba4120ec..aa801ddb 100644 --- a/src/grpc/pb/google/protobuf/struct.ts +++ b/src/grpc/pb/google/protobuf/struct.ts @@ -1,6 +1,6 @@ // Code generated by protoc-gen-ts_proto. DO NOT EDIT. // versions: -// protoc-gen-ts_proto v2.6.1 +// protoc-gen-ts_proto v2.11.0 // protoc unknown // source: google/protobuf/struct.proto @@ -89,7 +89,7 @@ function createBaseStruct(): Struct { export const Struct: MessageFns & StructWrapperFns = { encode(message: Struct, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - Object.entries(message.fields).forEach(([key, value]) => { + globalThis.Object.entries(message.fields).forEach(([key, value]: [string, any | undefined]) => { if (value !== undefined) { Struct_FieldsEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join(); } @@ -99,7 +99,7 @@ export const Struct: MessageFns & StructWrapperFns = { decode(input: BinaryReader | Uint8Array, length?: number): Struct { const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; + const end = length === undefined ? reader.len : reader.pos + length; const message = createBaseStruct(); while (reader.pos < end) { const tag = reader.uint32(); @@ -128,7 +128,7 @@ export const Struct: MessageFns & StructWrapperFns = { const struct = createBaseStruct(); if (object !== undefined) { - for (const key of Object.keys(object)) { + for (const key of globalThis.Object.keys(object)) { struct.fields[key] = object[key]; } } @@ -138,7 +138,7 @@ export const Struct: MessageFns & StructWrapperFns = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; if (message.fields) { - for (const key of Object.keys(message.fields)) { + for (const key of globalThis.Object.keys(message.fields)) { object[key] = message.fields[key]; } } @@ -163,7 +163,7 @@ export const Struct_FieldsEntry: MessageFns = { decode(input: BinaryReader | Uint8Array, length?: number): Struct_FieldsEntry { const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; + const end = length === undefined ? reader.len : reader.pos + length; const message = createBaseStruct_FieldsEntry(); while (reader.pos < end) { const tag = reader.uint32(); @@ -225,7 +225,7 @@ export const Value: MessageFns & AnyValueWrapperFns = { decode(input: BinaryReader | Uint8Array, length?: number): Value { const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; + const end = length === undefined ? reader.len : reader.pos + length; const message = createBaseValue(); while (reader.pos < end) { const tag = reader.uint32(); @@ -290,7 +290,7 @@ export const Value: MessageFns & AnyValueWrapperFns = { wrap(value: any): Value { const result = createBaseValue(); if (value === null) { - result.kind = { $case: "nullValue", value }; + result.kind = { $case: "nullValue", value: NullValue.NULL_VALUE }; } else if (typeof value === "boolean") { result.kind = { $case: "boolValue", value }; } else if (typeof value === "number") { @@ -308,7 +308,7 @@ export const Value: MessageFns & AnyValueWrapperFns = { }, unwrap(message: Value): string | number | boolean | Object | null | Array | undefined { - return message.kind?.value; + return (message.kind?.$case === "nullValue") ? null : message.kind?.value; }, }; @@ -326,7 +326,7 @@ export const ListValue: MessageFns & ListValueWrapperFns = { decode(input: BinaryReader | Uint8Array, length?: number): ListValue { const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; + const end = length === undefined ? reader.len : reader.pos + length; const message = createBaseListValue(); while (reader.pos < end) { const tag = reader.uint32(); diff --git a/test/server/grpc/protobuf_serialization.spec.ts b/test/server/grpc/protobuf_serialization.spec.ts new file mode 100644 index 00000000..6acadd93 --- /dev/null +++ b/test/server/grpc/protobuf_serialization.spec.ts @@ -0,0 +1,29 @@ +import { describe, expect, it } from 'vitest'; +import { A2AServiceService, SendMessageResponse } from '../../../src/grpc/pb/a2a.js'; + +describe('gRPC protobuf serialization', () => { + it('preserves null values in task metadata', () => { + const response: SendMessageResponse = { + payload: { + $case: 'task', + value: { + id: 'task-1', + contextId: 'context-1', + status: undefined, + artifacts: [], + history: [], + metadata: { + direct: null, + nested: { value: null }, + items: [null, 'present'], + }, + }, + }, + }; + + const serialized = A2AServiceService.sendMessage.responseSerialize(response); + const deserialized = A2AServiceService.sendMessage.responseDeserialize(serialized); + + expect(deserialized).toEqual(response); + }); +});