@yeaft/webchat-agent 0.1.197 → 0.1.198

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/sdk/query.js +62 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.197",
3
+ "version": "0.1.198",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/sdk/query.js CHANGED
@@ -70,10 +70,19 @@ export class Query {
70
70
 
71
71
  /**
72
72
  * Read messages from Claude process stdout
73
+ *
74
+ * With --include-partial-messages, the CLI emits stream_event messages
75
+ * containing incremental text deltas (content_block_delta / text_delta).
76
+ * We convert these to assistant-format messages for real-time streaming,
77
+ * then deduplicate when the final complete assistant message arrives.
73
78
  */
74
79
  async readMessages() {
75
80
  const rl = createInterface({ input: this.childStdout });
76
81
 
82
+ // Track whether we've forwarded text deltas for the current assistant turn.
83
+ // When true, the next complete `assistant` message's text blocks are redundant.
84
+ let hasStreamedTextDeltas = false;
85
+
77
86
  try {
78
87
  for await (const line of rl) {
79
88
  if (line.trim()) {
@@ -100,6 +109,58 @@ export class Query {
100
109
  continue;
101
110
  }
102
111
 
112
+ // Handle stream_event messages (from --include-partial-messages)
113
+ if (message.type === 'stream_event') {
114
+ const event = message.event;
115
+ if (!event) continue;
116
+
117
+ // content_block_delta with text_delta → convert to assistant message for streaming
118
+ if (event.type === 'content_block_delta' && event.delta?.type === 'text_delta' && event.delta.text) {
119
+ hasStreamedTextDeltas = true;
120
+ this.inputStream.enqueue({
121
+ type: 'assistant',
122
+ message: {
123
+ role: 'assistant',
124
+ content: [{ type: 'text', text: event.delta.text }]
125
+ }
126
+ });
127
+ }
128
+ // All other stream events (message_start, content_block_start,
129
+ // input_json_delta, content_block_stop, message_stop) are ignored.
130
+ // tool_use is handled via the complete assistant message.
131
+ continue;
132
+ }
133
+
134
+ // Deduplicate: when a complete assistant message arrives after we've
135
+ // already streamed text deltas, strip the text blocks (already sent).
136
+ // Keep tool_use blocks which are NOT sent incrementally.
137
+ if (message.type === 'assistant' && hasStreamedTextDeltas) {
138
+ hasStreamedTextDeltas = false; // Reset for next assistant turn
139
+
140
+ const content = message.message?.content;
141
+ if (Array.isArray(content)) {
142
+ const nonTextBlocks = content.filter(b => b.type !== 'text');
143
+ if (nonTextBlocks.length > 0) {
144
+ // Forward only tool_use blocks (text already sent via deltas)
145
+ message.message.content = nonTextBlocks;
146
+ this.inputStream.enqueue(message);
147
+ }
148
+ // If only text blocks: skip entirely (all content already streamed)
149
+ } else if (typeof content === 'string') {
150
+ // String content was already streamed — skip
151
+ } else {
152
+ // Unknown format — forward as-is to be safe
153
+ this.inputStream.enqueue(message);
154
+ }
155
+ continue;
156
+ }
157
+
158
+ // Reset delta tracking on non-assistant messages
159
+ // (e.g., user, result, system — a new turn boundary)
160
+ if (message.type !== 'assistant') {
161
+ hasStreamedTextDeltas = false;
162
+ }
163
+
103
164
  this.inputStream.enqueue(message);
104
165
  } catch (e) {
105
166
  logDebug(`Non-JSON line: ${line.substring(0, 100)}`);
@@ -284,7 +345,7 @@ export function query(config) {
284
345
  } = config;
285
346
 
286
347
  // Build command arguments
287
- const args = ['--output-format', 'stream-json', '--verbose'];
348
+ const args = ['--output-format', 'stream-json', '--verbose', '--include-partial-messages'];
288
349
 
289
350
  if (customSystemPrompt) args.push('--system-prompt', customSystemPrompt);
290
351
  if (appendSystemPrompt) args.push('--append-system-prompt', appendSystemPrompt);