march-ai-sdk 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +732 -0
- package/dist/app-C_umwZXh.d.ts +790 -0
- package/dist/extensions/langgraph.d.ts +144 -0
- package/dist/extensions/langgraph.js +326 -0
- package/dist/extensions/langgraph.js.map +1 -0
- package/dist/extensions/vercel-ai.d.ts +124 -0
- package/dist/extensions/vercel-ai.js +177 -0
- package/dist/extensions/vercel-ai.js.map +1 -0
- package/dist/index.d.ts +260 -0
- package/dist/index.js +1695 -0
- package/dist/index.js.map +1 -0
- package/dist/proto/gateway.proto +99 -0
- package/package.json +83 -0
- package/src/agent-state-client.ts +115 -0
- package/src/agent.ts +293 -0
- package/src/api-paths.ts +60 -0
- package/src/app.ts +235 -0
- package/src/artifact.ts +59 -0
- package/src/attachment-client.ts +78 -0
- package/src/checkpoint-client.ts +175 -0
- package/src/conversation-client.ts +109 -0
- package/src/conversation-message.ts +61 -0
- package/src/conversation.ts +123 -0
- package/src/exceptions.ts +78 -0
- package/src/extensions/index.ts +6 -0
- package/src/extensions/langgraph.ts +351 -0
- package/src/extensions/vercel-ai.ts +177 -0
- package/src/gateway-client.ts +420 -0
- package/src/heartbeat.ts +89 -0
- package/src/index.ts +70 -0
- package/src/memory-client.ts +125 -0
- package/src/memory.ts +68 -0
- package/src/message.ts +178 -0
- package/src/proto/gateway.proto +99 -0
- package/src/streamer.ts +242 -0
- package/src/types.ts +196 -0
package/src/memory.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* March Agent SDK - Memory Helper
|
|
3
|
+
* Port of Python march_agent/memory.py
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { MemoryClient } from './memory-client.js'
|
|
7
|
+
import type { MemoryMessage, MemorySearchResult, UserSummary } from './types.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Helper class for accessing long-term memory.
|
|
11
|
+
* Provides convenient methods for storing and searching memories.
|
|
12
|
+
*/
|
|
13
|
+
export class Memory {
|
|
14
|
+
readonly userId: string
|
|
15
|
+
readonly conversationId: string
|
|
16
|
+
private readonly client: MemoryClient
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
userId: string,
|
|
20
|
+
conversationId: string,
|
|
21
|
+
client: MemoryClient
|
|
22
|
+
) {
|
|
23
|
+
this.userId = userId
|
|
24
|
+
this.conversationId = conversationId
|
|
25
|
+
this.client = client
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Add messages to memory.
|
|
30
|
+
*/
|
|
31
|
+
async addMessages(messages: MemoryMessage[]): Promise<number> {
|
|
32
|
+
const result = await this.client.addMessages(
|
|
33
|
+
this.userId,
|
|
34
|
+
this.conversationId,
|
|
35
|
+
messages
|
|
36
|
+
)
|
|
37
|
+
return result.added
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Add a single message to memory.
|
|
42
|
+
*/
|
|
43
|
+
async addMessage(role: 'user' | 'assistant', content: string): Promise<void> {
|
|
44
|
+
await this.addMessages([{ role, content }])
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Search memory for relevant content.
|
|
49
|
+
*/
|
|
50
|
+
async search(query: string, limit: number = 10): Promise<MemorySearchResult[]> {
|
|
51
|
+
return this.client.search(this.userId, query, limit)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get user summary.
|
|
56
|
+
*/
|
|
57
|
+
async getSummary(): Promise<UserSummary | null> {
|
|
58
|
+
return this.client.getUserSummary(this.userId)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Clear all memories for this user.
|
|
63
|
+
*/
|
|
64
|
+
async clear(): Promise<number> {
|
|
65
|
+
const result = await this.client.clearMemory(this.userId)
|
|
66
|
+
return result.deleted
|
|
67
|
+
}
|
|
68
|
+
}
|
package/src/message.ts
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* March Agent SDK - Message
|
|
3
|
+
* Port of Python march_agent/message.py
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Conversation } from './conversation.js'
|
|
7
|
+
import { Memory } from './memory.js'
|
|
8
|
+
import { ConversationClient } from './conversation-client.js'
|
|
9
|
+
import { MemoryClient } from './memory-client.js'
|
|
10
|
+
import { AttachmentClient, createAttachmentInfo } from './attachment-client.js'
|
|
11
|
+
import type { AttachmentInfo, KafkaHeaders } from './types.js'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Represents an incoming message to the agent.
|
|
15
|
+
*/
|
|
16
|
+
export class Message {
|
|
17
|
+
readonly content: string
|
|
18
|
+
readonly conversationId: string
|
|
19
|
+
readonly userId: string
|
|
20
|
+
readonly headers: Record<string, string>
|
|
21
|
+
readonly rawBody: Record<string, unknown>
|
|
22
|
+
readonly conversation?: Conversation
|
|
23
|
+
readonly memory?: Memory
|
|
24
|
+
readonly metadata?: Record<string, unknown>
|
|
25
|
+
readonly schema?: Record<string, unknown>
|
|
26
|
+
readonly attachment?: AttachmentInfo
|
|
27
|
+
|
|
28
|
+
private readonly attachmentClient?: AttachmentClient
|
|
29
|
+
|
|
30
|
+
constructor(options: {
|
|
31
|
+
content: string
|
|
32
|
+
conversationId: string
|
|
33
|
+
userId: string
|
|
34
|
+
headers: Record<string, string>
|
|
35
|
+
rawBody: Record<string, unknown>
|
|
36
|
+
conversation?: Conversation
|
|
37
|
+
memory?: Memory
|
|
38
|
+
metadata?: Record<string, unknown>
|
|
39
|
+
schema?: Record<string, unknown>
|
|
40
|
+
attachment?: AttachmentInfo
|
|
41
|
+
attachmentClient?: AttachmentClient
|
|
42
|
+
}) {
|
|
43
|
+
this.content = options.content
|
|
44
|
+
this.conversationId = options.conversationId
|
|
45
|
+
this.userId = options.userId
|
|
46
|
+
this.headers = options.headers
|
|
47
|
+
this.rawBody = options.rawBody
|
|
48
|
+
this.conversation = options.conversation
|
|
49
|
+
this.memory = options.memory
|
|
50
|
+
this.metadata = options.metadata
|
|
51
|
+
this.schema = options.schema
|
|
52
|
+
this.attachment = options.attachment
|
|
53
|
+
this.attachmentClient = options.attachmentClient
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Create Message from Kafka message data.
|
|
58
|
+
*/
|
|
59
|
+
static fromKafkaMessage(
|
|
60
|
+
body: Record<string, unknown>,
|
|
61
|
+
headers: KafkaHeaders,
|
|
62
|
+
options: {
|
|
63
|
+
conversationClient?: ConversationClient
|
|
64
|
+
memoryClient?: MemoryClient
|
|
65
|
+
attachmentClient?: AttachmentClient
|
|
66
|
+
agentName?: string
|
|
67
|
+
} = {}
|
|
68
|
+
): Message {
|
|
69
|
+
const conversationId = headers.conversationId ?? ''
|
|
70
|
+
const userId = headers.userId ?? 'anonymous'
|
|
71
|
+
|
|
72
|
+
// Parse metadata from header
|
|
73
|
+
let metadata: Record<string, unknown> | undefined
|
|
74
|
+
if (headers.messageMetadata) {
|
|
75
|
+
try {
|
|
76
|
+
metadata = JSON.parse(headers.messageMetadata)
|
|
77
|
+
} catch {
|
|
78
|
+
// Ignore parse errors
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Parse schema from header
|
|
83
|
+
let schema: Record<string, unknown> | undefined
|
|
84
|
+
if (headers.messageSchema) {
|
|
85
|
+
try {
|
|
86
|
+
schema = JSON.parse(headers.messageSchema)
|
|
87
|
+
} catch {
|
|
88
|
+
// Ignore parse errors
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Parse attachment from header or body
|
|
93
|
+
let attachment: AttachmentInfo | undefined
|
|
94
|
+
if (headers.attachment) {
|
|
95
|
+
try {
|
|
96
|
+
const attachmentData = JSON.parse(headers.attachment)
|
|
97
|
+
attachment = createAttachmentInfo(attachmentData)
|
|
98
|
+
} catch {
|
|
99
|
+
// Ignore parse errors
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Fallback to body attachment
|
|
103
|
+
if (!attachment && body.attachment) {
|
|
104
|
+
try {
|
|
105
|
+
attachment = createAttachmentInfo(body.attachment as Record<string, unknown>)
|
|
106
|
+
} catch {
|
|
107
|
+
// Ignore parse errors
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Create conversation helper
|
|
112
|
+
let conversation: Conversation | undefined
|
|
113
|
+
if (conversationId && options.conversationClient) {
|
|
114
|
+
conversation = new Conversation(
|
|
115
|
+
conversationId,
|
|
116
|
+
options.conversationClient,
|
|
117
|
+
options.agentName
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Create memory helper
|
|
122
|
+
let memory: Memory | undefined
|
|
123
|
+
if (options.memoryClient && userId && conversationId) {
|
|
124
|
+
memory = new Memory(userId, conversationId, options.memoryClient)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return new Message({
|
|
128
|
+
content: (body.content as string) ?? '',
|
|
129
|
+
conversationId,
|
|
130
|
+
userId,
|
|
131
|
+
headers: headers as Record<string, string>,
|
|
132
|
+
rawBody: body,
|
|
133
|
+
conversation,
|
|
134
|
+
memory,
|
|
135
|
+
metadata,
|
|
136
|
+
schema,
|
|
137
|
+
attachment,
|
|
138
|
+
attachmentClient: options.attachmentClient,
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Check if message has an attachment.
|
|
144
|
+
*/
|
|
145
|
+
hasAttachment(): boolean {
|
|
146
|
+
return this.attachment !== undefined
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Download attachment as bytes (Buffer).
|
|
151
|
+
*
|
|
152
|
+
* @throws Error if no attachment is available
|
|
153
|
+
*/
|
|
154
|
+
async getAttachmentBytes(): Promise<Buffer> {
|
|
155
|
+
if (!this.attachment) {
|
|
156
|
+
throw new Error('No attachment available')
|
|
157
|
+
}
|
|
158
|
+
if (!this.attachmentClient) {
|
|
159
|
+
throw new Error('AttachmentClient not available')
|
|
160
|
+
}
|
|
161
|
+
return this.attachmentClient.download(this.attachment.url)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get attachment as base64 string (for LLM vision APIs).
|
|
166
|
+
*
|
|
167
|
+
* @throws Error if no attachment is available
|
|
168
|
+
*/
|
|
169
|
+
async getAttachmentBase64(): Promise<string> {
|
|
170
|
+
if (!this.attachment) {
|
|
171
|
+
throw new Error('No attachment available')
|
|
172
|
+
}
|
|
173
|
+
if (!this.attachmentClient) {
|
|
174
|
+
throw new Error('AttachmentClient not available')
|
|
175
|
+
}
|
|
176
|
+
return this.attachmentClient.downloadAsBase64(this.attachment.url)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
syntax = "proto3";
|
|
2
|
+
package gateway;
|
|
3
|
+
option go_package = "agent-gateway/internal/grpc/pb";
|
|
4
|
+
|
|
5
|
+
// Bidirectional streaming service for agent communication
|
|
6
|
+
service AgentGateway {
|
|
7
|
+
// Main bidirectional stream for all agent communication
|
|
8
|
+
rpc AgentStream(stream ClientMessage) returns (stream ServerMessage);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Client -> Server messages
|
|
12
|
+
message ClientMessage {
|
|
13
|
+
oneof payload {
|
|
14
|
+
AuthRequest auth = 1;
|
|
15
|
+
SubscribeRequest subscribe = 2;
|
|
16
|
+
UnsubscribeRequest unsubscribe = 3;
|
|
17
|
+
ProduceRequest produce = 4;
|
|
18
|
+
PingRequest ping = 5;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Server -> Client messages
|
|
23
|
+
message ServerMessage {
|
|
24
|
+
oneof payload {
|
|
25
|
+
AuthResponse auth_response = 1;
|
|
26
|
+
KafkaMessage message = 2;
|
|
27
|
+
ProduceAck produce_ack = 3;
|
|
28
|
+
SubscribeAck subscribe_ack = 4;
|
|
29
|
+
UnsubscribeAck unsubscribe_ack = 5;
|
|
30
|
+
PongResponse pong = 6;
|
|
31
|
+
ErrorResponse error = 7;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
message AuthRequest {
|
|
36
|
+
string api_key = 1;
|
|
37
|
+
repeated string agent_names = 2;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
message AuthResponse {
|
|
41
|
+
string connection_id = 1;
|
|
42
|
+
repeated string subscribed_topics = 2;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
message SubscribeRequest {
|
|
46
|
+
string agent_name = 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
message SubscribeAck {
|
|
50
|
+
string topic = 1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
message UnsubscribeRequest {
|
|
54
|
+
string agent_name = 1;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
message UnsubscribeAck {
|
|
58
|
+
string agent_name = 1;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
message ProduceRequest {
|
|
62
|
+
string topic = 1;
|
|
63
|
+
string key = 2;
|
|
64
|
+
map<string, string> headers = 3;
|
|
65
|
+
bytes body = 4; // JSON as bytes
|
|
66
|
+
string correlation_id = 5;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
message ProduceAck {
|
|
70
|
+
string topic = 1;
|
|
71
|
+
int32 partition = 2;
|
|
72
|
+
int64 offset = 3;
|
|
73
|
+
string correlation_id = 4;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
message KafkaMessage {
|
|
77
|
+
string topic = 1;
|
|
78
|
+
int32 partition = 2;
|
|
79
|
+
int64 offset = 3;
|
|
80
|
+
string key = 4;
|
|
81
|
+
map<string, string> headers = 5;
|
|
82
|
+
bytes body = 6; // JSON as bytes
|
|
83
|
+
int64 timestamp = 7;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
message PingRequest {
|
|
87
|
+
int64 timestamp = 1;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
message PongResponse {
|
|
91
|
+
int64 client_timestamp = 1;
|
|
92
|
+
int64 server_timestamp = 2;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
message ErrorResponse {
|
|
96
|
+
string code = 1;
|
|
97
|
+
string message = 2;
|
|
98
|
+
string correlation_id = 3;
|
|
99
|
+
}
|
package/src/streamer.ts
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* March Agent SDK - Streamer
|
|
3
|
+
* Port of Python march_agent/streamer.py
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Message } from './message.js'
|
|
7
|
+
import type { GatewayClient } from './gateway-client.js'
|
|
8
|
+
import type { ConversationClient } from './conversation-client.js'
|
|
9
|
+
import type { Artifact, ArtifactInput } from './artifact.js'
|
|
10
|
+
import { toArtifact } from './artifact.js'
|
|
11
|
+
import type { StreamOptions } from './types.js'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Handles streaming responses back to the conversation via the gateway.
|
|
15
|
+
*/
|
|
16
|
+
export class Streamer {
|
|
17
|
+
private readonly agentName: string
|
|
18
|
+
private readonly originalMessage: Message
|
|
19
|
+
private readonly gatewayClient: GatewayClient
|
|
20
|
+
private readonly conversationClient?: ConversationClient
|
|
21
|
+
private readonly sendTo: string
|
|
22
|
+
private awaiting: boolean
|
|
23
|
+
|
|
24
|
+
private responseSchema?: Record<string, unknown>
|
|
25
|
+
private messageMetadata?: Record<string, unknown>
|
|
26
|
+
private artifacts: Artifact[] = []
|
|
27
|
+
private streamedContent: string = ''
|
|
28
|
+
private firstChunkSent: boolean = false
|
|
29
|
+
private finished: boolean = false
|
|
30
|
+
|
|
31
|
+
constructor(options: {
|
|
32
|
+
agentName: string
|
|
33
|
+
originalMessage: Message
|
|
34
|
+
gatewayClient: GatewayClient
|
|
35
|
+
conversationClient?: ConversationClient
|
|
36
|
+
awaiting?: boolean
|
|
37
|
+
sendTo?: string
|
|
38
|
+
}) {
|
|
39
|
+
this.agentName = options.agentName
|
|
40
|
+
this.originalMessage = options.originalMessage
|
|
41
|
+
this.gatewayClient = options.gatewayClient
|
|
42
|
+
this.conversationClient = options.conversationClient
|
|
43
|
+
this.awaiting = options.awaiting ?? false
|
|
44
|
+
this.sendTo = options.sendTo ?? 'user'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Set response schema for form rendering (fluent API).
|
|
49
|
+
*/
|
|
50
|
+
setResponseSchema(schema: Record<string, unknown>): this {
|
|
51
|
+
this.responseSchema = schema
|
|
52
|
+
return this
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Set message metadata (fluent API).
|
|
57
|
+
*/
|
|
58
|
+
setMessageMetadata(metadata: Record<string, unknown>): this {
|
|
59
|
+
this.messageMetadata = metadata
|
|
60
|
+
return this
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Add an artifact to the message (fluent API).
|
|
65
|
+
*/
|
|
66
|
+
addArtifact(artifact: ArtifactInput): this {
|
|
67
|
+
this.artifacts.push(toArtifact(artifact))
|
|
68
|
+
return this
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Set all artifacts at once (replaces any existing).
|
|
73
|
+
*/
|
|
74
|
+
setArtifacts(artifacts: ArtifactInput[]): this {
|
|
75
|
+
this.artifacts = artifacts.map(toArtifact)
|
|
76
|
+
return this
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Stream a content chunk.
|
|
81
|
+
*/
|
|
82
|
+
stream(content: string, options: StreamOptions = {}): void {
|
|
83
|
+
const { persist = true, eventType } = options
|
|
84
|
+
|
|
85
|
+
if (this.finished) {
|
|
86
|
+
console.warn('Streamer.stream() called after finish()')
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (persist) {
|
|
91
|
+
this.streamedContent += content
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.send(content, false, persist, eventType)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Alias for stream() - write a content chunk.
|
|
99
|
+
*/
|
|
100
|
+
write(content: string, persist: boolean = true): void {
|
|
101
|
+
this.stream(content, { persist })
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Finish streaming with done=true signal.
|
|
106
|
+
*/
|
|
107
|
+
async finish(awaitingOverride?: boolean): Promise<void> {
|
|
108
|
+
if (this.finished) {
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this.finished = true
|
|
113
|
+
|
|
114
|
+
// Determine final awaiting value
|
|
115
|
+
let finalAwaiting = awaitingOverride ?? this.awaiting
|
|
116
|
+
|
|
117
|
+
// If response schema was set and awaiting not explicitly false, set awaiting
|
|
118
|
+
if (this.responseSchema && awaitingOverride !== false) {
|
|
119
|
+
finalAwaiting = true
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Send final done message
|
|
123
|
+
this.send('', true, false)
|
|
124
|
+
|
|
125
|
+
// Set pending response schema on conversation
|
|
126
|
+
if (this.responseSchema && this.conversationClient) {
|
|
127
|
+
await this.setPendingResponseSchema()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Set awaiting route
|
|
131
|
+
if (finalAwaiting && this.conversationClient) {
|
|
132
|
+
await this.setAwaitingRoute()
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Send message to router via gateway.
|
|
138
|
+
*/
|
|
139
|
+
private send(
|
|
140
|
+
content: string,
|
|
141
|
+
done: boolean,
|
|
142
|
+
persist: boolean = true,
|
|
143
|
+
eventType?: string
|
|
144
|
+
): void {
|
|
145
|
+
// Build headers (matching Python implementation)
|
|
146
|
+
const headers: Record<string, string> = {
|
|
147
|
+
conversationId: this.originalMessage.conversationId,
|
|
148
|
+
userId: this.originalMessage.userId,
|
|
149
|
+
from_: this.agentName,
|
|
150
|
+
to_: this.sendTo,
|
|
151
|
+
nextRoute: this.sendTo,
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (eventType) {
|
|
155
|
+
headers.eventType = eventType
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Include metadata, artifacts, and schema on first chunk (only once)
|
|
159
|
+
if (!this.firstChunkSent) {
|
|
160
|
+
this.firstChunkSent = true
|
|
161
|
+
|
|
162
|
+
if (this.messageMetadata) {
|
|
163
|
+
headers.messageMetadata = JSON.stringify(this.messageMetadata)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (this.artifacts.length > 0) {
|
|
167
|
+
headers.artifacts = JSON.stringify(this.artifacts)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (this.responseSchema) {
|
|
171
|
+
headers.responseSchema = JSON.stringify(this.responseSchema)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Build body (matching Python implementation - includes persist)
|
|
176
|
+
const body: Record<string, unknown> = {
|
|
177
|
+
content,
|
|
178
|
+
done,
|
|
179
|
+
persist,
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (eventType) {
|
|
183
|
+
body.eventType = eventType
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Produce message via gateway
|
|
187
|
+
this.gatewayClient.produce(
|
|
188
|
+
'router.inbox',
|
|
189
|
+
this.originalMessage.conversationId,
|
|
190
|
+
headers,
|
|
191
|
+
body
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Store response schema on conversation for form validation.
|
|
197
|
+
*/
|
|
198
|
+
private async setPendingResponseSchema(): Promise<void> {
|
|
199
|
+
if (!this.conversationClient || !this.responseSchema) return
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
await this.conversationClient.updateConversation(
|
|
203
|
+
this.originalMessage.conversationId,
|
|
204
|
+
{ pendingResponseSchema: this.responseSchema } as never
|
|
205
|
+
)
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.error('Failed to set pending response schema:', error)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Set awaiting_route to this agent's name.
|
|
213
|
+
*/
|
|
214
|
+
private async setAwaitingRoute(): Promise<void> {
|
|
215
|
+
if (!this.conversationClient) return
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
await this.conversationClient.updateConversation(
|
|
219
|
+
this.originalMessage.conversationId,
|
|
220
|
+
{ awaitingRoute: this.agentName } as never
|
|
221
|
+
)
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.error('Failed to set awaiting route:', error)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Get the accumulated streamed content.
|
|
229
|
+
*/
|
|
230
|
+
getStreamedContent(): string {
|
|
231
|
+
return this.streamedContent
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Support for async disposal (TypeScript 5.2+ "using" syntax).
|
|
236
|
+
*/
|
|
237
|
+
async [Symbol.asyncDispose](): Promise<void> {
|
|
238
|
+
if (!this.finished) {
|
|
239
|
+
await this.finish()
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|