Skip to content

Commit 0436179

Browse files
committed
feat(appserver): stream real agent message deltas
1 parent e920bed commit 0436179

1 file changed

Lines changed: 23 additions & 0 deletions

File tree

src/main/java/io/github/easy4j/codex/appserver/CodexAppServerTurn.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.List;
2525
import java.util.Map;
2626
import java.util.Objects;
27+
import java.util.Set;
2728
import java.util.concurrent.CompletableFuture;
2829
import java.util.concurrent.CompletionException;
2930
import java.util.concurrent.CompletionStage;
@@ -74,6 +75,7 @@ class CodexAppServerTurn implements WebSocket.Listener {
7475
private final AtomicLong rpcIds = new AtomicLong();
7576
private final StringBuilder content = new StringBuilder();
7677
private final StringBuilder frameBuffer = new StringBuilder();
78+
private final Set<String> streamedAgentItemIds = ConcurrentHashMap.newKeySet();
7779
/** Records outgoing RPC payloads; without a socket they are only recorded, for contract tests. */
7880
private final List<String> sentMessages = new ArrayList<>();
7981

@@ -246,6 +248,7 @@ void handleFrame(String frame) {
246248
JsonNode params = node.path("params");
247249
switch (method) {
248250
case CodexAppServerProtocol.TURN_STARTED -> onTurnStarted(params);
251+
case CodexAppServerProtocol.AGENT_MESSAGE_DELTA -> onAgentMessageDelta(params);
249252
case CodexAppServerProtocol.ITEM_COMPLETED -> onItemCompleted(params);
250253
case CodexAppServerProtocol.TURN_COMPLETED -> onTurnCompleted(params);
251254
case CodexAppServerProtocol.TURN_FAILED -> completeError(new CodexAppServerException(
@@ -266,6 +269,22 @@ private void onTurnStarted(JsonNode params) {
266269
}
267270
}
268271

272+
private void onAgentMessageDelta(JsonNode params) {
273+
String delta = firstText(params, "delta");
274+
if (!hasText(delta)) {
275+
return;
276+
}
277+
String itemId = firstText(params, "itemId", "item_id");
278+
if (hasText(itemId)) {
279+
streamedAgentItemIds.add(itemId);
280+
}
281+
String applied = truncateToContentCap(delta);
282+
content.append(applied);
283+
if (!applied.isEmpty() && Objects.nonNull(request.getOnDelta())) {
284+
request.getOnDelta().accept(applied);
285+
}
286+
}
287+
269288
private void onItemCompleted(JsonNode params) {
270289
JsonNode item = params.path("item");
271290
String type = firstText(item, "type", "itemType");
@@ -277,6 +296,10 @@ private void onItemCompleted(JsonNode params) {
277296
if (!hasText(text)) {
278297
return;
279298
}
299+
String itemId = firstText(item, "id", "itemId", "item_id");
300+
if (hasText(itemId) && streamedAgentItemIds.contains(itemId)) {
301+
return;
302+
}
280303
String applied = truncateToContentCap(text);
281304
content.append(applied);
282305
if (!applied.isEmpty() && Objects.nonNull(request.getOnDelta())) {

0 commit comments

Comments
 (0)