@getpaseo/server 0.1.14 → 0.1.16
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/dist/server/client/daemon-client.d.ts +41 -4
- package/dist/server/client/daemon-client.d.ts.map +1 -1
- package/dist/server/client/daemon-client.js +355 -84
- package/dist/server/client/daemon-client.js.map +1 -1
- package/dist/server/server/agent/agent-manager.d.ts +10 -0
- package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
- package/dist/server/server/agent/agent-manager.js +261 -18
- package/dist/server/server/agent/agent-manager.js.map +1 -1
- package/dist/server/server/agent/agent-projections.d.ts +5 -0
- package/dist/server/server/agent/agent-projections.d.ts.map +1 -1
- package/dist/server/server/agent/agent-projections.js +24 -0
- package/dist/server/server/agent/agent-projections.js.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts +11 -0
- package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
- package/dist/server/server/agent/agent-storage.d.ts +15 -5
- package/dist/server/server/agent/agent-storage.d.ts.map +1 -1
- package/dist/server/server/agent/agent-storage.js +2 -0
- package/dist/server/server/agent/agent-storage.js.map +1 -1
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js +2 -0
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js.map +1 -1
- package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude/tool-call-mapper.js +2 -0
- package/dist/server/server/agent/providers/claude/tool-call-mapper.js.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.d.ts +7 -1
- package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.js +1470 -237
- package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.js +19 -4
- package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +40 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
- package/dist/server/server/agent/providers/tool-call-detail-primitives.js +1 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.js.map +1 -1
- package/dist/server/server/client-message-id.d.ts +3 -0
- package/dist/server/server/client-message-id.d.ts.map +1 -0
- package/dist/server/server/client-message-id.js +12 -0
- package/dist/server/server/client-message-id.js.map +1 -0
- package/dist/server/server/persisted-config.d.ts +8 -8
- package/dist/server/server/persistence-hooks.js +1 -1
- package/dist/server/server/persistence-hooks.js.map +1 -1
- package/dist/server/server/relay-transport.d.ts.map +1 -1
- package/dist/server/server/relay-transport.js +27 -28
- package/dist/server/server/relay-transport.js.map +1 -1
- package/dist/server/server/session.d.ts +4 -2
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +122 -31
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/server/websocket-server.d.ts +8 -4
- package/dist/server/server/websocket-server.d.ts.map +1 -1
- package/dist/server/server/websocket-server.js +272 -75
- package/dist/server/server/websocket-server.js.map +1 -1
- package/dist/server/shared/daemon-endpoints.d.ts +9 -1
- package/dist/server/shared/daemon-endpoints.d.ts.map +1 -1
- package/dist/server/shared/daemon-endpoints.js +18 -3
- package/dist/server/shared/daemon-endpoints.js.map +1 -1
- package/dist/server/shared/messages.d.ts +2065 -313
- package/dist/server/shared/messages.d.ts.map +1 -1
- package/dist/server/shared/messages.js +40 -1
- package/dist/server/shared/messages.js.map +1 -1
- package/dist/server/shared/tool-call-display.d.ts.map +1 -1
- package/dist/server/shared/tool-call-display.js +4 -0
- package/dist/server/shared/tool-call-display.js.map +1 -1
- package/package.json +3 -3
|
@@ -19,6 +19,7 @@ function attachPersistenceCwd(handle, cwd) {
|
|
|
19
19
|
}
|
|
20
20
|
const DEFAULT_MAX_TIMELINE_ITEMS = 2000;
|
|
21
21
|
const DEFAULT_TIMELINE_FETCH_LIMIT = 200;
|
|
22
|
+
const LIVE_BACKLOG_TERMINAL_REPLAY_DELAY_MS = 300;
|
|
22
23
|
const BUSY_STATUSES = [
|
|
23
24
|
"initializing",
|
|
24
25
|
"running",
|
|
@@ -27,6 +28,11 @@ const AgentIdSchema = z.string().uuid();
|
|
|
27
28
|
function isAgentBusy(status) {
|
|
28
29
|
return BUSY_STATUSES.includes(status);
|
|
29
30
|
}
|
|
31
|
+
function isTurnTerminalEvent(event) {
|
|
32
|
+
return (event.type === "turn_completed" ||
|
|
33
|
+
event.type === "turn_failed" ||
|
|
34
|
+
event.type === "turn_canceled");
|
|
35
|
+
}
|
|
30
36
|
function createAbortError(signal, fallbackMessage) {
|
|
31
37
|
const reason = signal?.reason;
|
|
32
38
|
const message = typeof reason === "string"
|
|
@@ -43,6 +49,18 @@ function validateAgentId(agentId, source) {
|
|
|
43
49
|
}
|
|
44
50
|
return result.data;
|
|
45
51
|
}
|
|
52
|
+
function supportsLiveEventStream(session) {
|
|
53
|
+
return ("streamLiveEvents" in session &&
|
|
54
|
+
typeof session.streamLiveEvents ===
|
|
55
|
+
"function");
|
|
56
|
+
}
|
|
57
|
+
function normalizeMessageId(messageId) {
|
|
58
|
+
if (typeof messageId !== "string") {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
const trimmed = messageId.trim();
|
|
62
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
63
|
+
}
|
|
46
64
|
export class AgentManager {
|
|
47
65
|
constructor(options) {
|
|
48
66
|
this.clients = new Map();
|
|
@@ -50,6 +68,9 @@ export class AgentManager {
|
|
|
50
68
|
this.subscribers = new Set();
|
|
51
69
|
this.previousStatuses = new Map();
|
|
52
70
|
this.backgroundTasks = new Set();
|
|
71
|
+
this.liveEventPumps = new Map();
|
|
72
|
+
this.liveEventBacklog = new Map();
|
|
73
|
+
this.liveEventBacklogFlushTimers = new Map();
|
|
53
74
|
this.maxTimelineItems =
|
|
54
75
|
options?.maxTimelineItems ?? DEFAULT_MAX_TIMELINE_ITEMS;
|
|
55
76
|
this.idFactory = options?.idFactory ?? (() => randomUUID());
|
|
@@ -397,6 +418,9 @@ export class AgentManager {
|
|
|
397
418
|
: await client.createSession(normalizedConfig);
|
|
398
419
|
// Remove the existing agent entry before swapping sessions
|
|
399
420
|
this.agents.delete(agentId);
|
|
421
|
+
this.liveEventPumps.delete(agentId);
|
|
422
|
+
this.liveEventBacklog.delete(agentId);
|
|
423
|
+
this.clearLiveEventBacklogFlushTimer(agentId);
|
|
400
424
|
try {
|
|
401
425
|
await existing.session.close();
|
|
402
426
|
}
|
|
@@ -422,6 +446,9 @@ export class AgentManager {
|
|
|
422
446
|
async closeAgent(agentId) {
|
|
423
447
|
const agent = this.requireAgent(agentId);
|
|
424
448
|
this.agents.delete(agentId);
|
|
449
|
+
this.liveEventPumps.delete(agentId);
|
|
450
|
+
this.liveEventBacklog.delete(agentId);
|
|
451
|
+
this.clearLiveEventBacklogFlushTimer(agentId);
|
|
425
452
|
// Clean up previousStatus to prevent memory leak
|
|
426
453
|
this.previousStatuses.delete(agentId);
|
|
427
454
|
const session = agent.session;
|
|
@@ -529,10 +556,11 @@ export class AgentManager {
|
|
|
529
556
|
}
|
|
530
557
|
recordUserMessage(agentId, text, options) {
|
|
531
558
|
const agent = this.requireAgent(agentId);
|
|
559
|
+
const normalizedMessageId = normalizeMessageId(options?.messageId);
|
|
532
560
|
const item = {
|
|
533
561
|
type: "user_message",
|
|
534
562
|
text,
|
|
535
|
-
messageId:
|
|
563
|
+
messageId: normalizedMessageId,
|
|
536
564
|
};
|
|
537
565
|
const updatedAt = this.touchUpdatedAt(agent);
|
|
538
566
|
agent.lastUserMessageAt = updatedAt;
|
|
@@ -604,6 +632,7 @@ export class AgentManager {
|
|
|
604
632
|
mutableAgent.persistence = attachPersistenceCwd(persistenceHandle, mutableAgent.cwd);
|
|
605
633
|
}
|
|
606
634
|
this.emitState(mutableAgent);
|
|
635
|
+
this.flushLiveEventBacklog(mutableAgent);
|
|
607
636
|
};
|
|
608
637
|
const self = this;
|
|
609
638
|
const streamForwarder = (async function* streamForwarder() {
|
|
@@ -726,7 +755,9 @@ export class AgentManager {
|
|
|
726
755
|
async cancelAgentRun(agentId) {
|
|
727
756
|
const agent = this.requireAgent(agentId);
|
|
728
757
|
const pendingRun = agent.pendingRun;
|
|
729
|
-
|
|
758
|
+
const hasForegroundPendingRun = Boolean(pendingRun) && typeof pendingRun?.return === "function";
|
|
759
|
+
const isAutonomousRunning = agent.lifecycle === "running" && !hasForegroundPendingRun;
|
|
760
|
+
if (!hasForegroundPendingRun && !isAutonomousRunning) {
|
|
730
761
|
return false;
|
|
731
762
|
}
|
|
732
763
|
try {
|
|
@@ -735,14 +766,16 @@ export class AgentManager {
|
|
|
735
766
|
catch (error) {
|
|
736
767
|
this.logger.error({ err: error, agentId }, "Failed to interrupt session");
|
|
737
768
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
769
|
+
if (hasForegroundPendingRun && pendingRun) {
|
|
770
|
+
try {
|
|
771
|
+
// Await the generator's .return() to ensure the finally block runs
|
|
772
|
+
// and pendingRun is properly cleared before we return.
|
|
773
|
+
await pendingRun.return(undefined);
|
|
774
|
+
}
|
|
775
|
+
catch (error) {
|
|
776
|
+
this.logger.error({ err: error, agentId }, "Failed to cancel run");
|
|
777
|
+
throw error;
|
|
778
|
+
}
|
|
746
779
|
}
|
|
747
780
|
// Clear any pending permissions that weren't cleaned up by handleStreamEvent.
|
|
748
781
|
// Due to microtask ordering, .return() may force the generator to its finally
|
|
@@ -841,6 +874,7 @@ export class AgentManager {
|
|
|
841
874
|
}
|
|
842
875
|
let currentStatus = initialStatus;
|
|
843
876
|
let hasStarted = initialBusy || hasPendingRun;
|
|
877
|
+
let terminalStatusOverride = null;
|
|
844
878
|
// Bug #3 Fix: Declare unsubscribe and abortHandler upfront so cleanup can reference them
|
|
845
879
|
let unsubscribe = null;
|
|
846
880
|
let abortHandler = null;
|
|
@@ -898,6 +932,9 @@ export class AgentManager {
|
|
|
898
932
|
return;
|
|
899
933
|
}
|
|
900
934
|
if (!waitForActive || hasStarted) {
|
|
935
|
+
if (terminalStatusOverride) {
|
|
936
|
+
currentStatus = terminalStatusOverride;
|
|
937
|
+
}
|
|
901
938
|
finish(null);
|
|
902
939
|
}
|
|
903
940
|
return;
|
|
@@ -908,20 +945,15 @@ export class AgentManager {
|
|
|
908
945
|
return;
|
|
909
946
|
}
|
|
910
947
|
if (event.event.type === "turn_failed") {
|
|
911
|
-
currentStatus = "error";
|
|
912
948
|
hasStarted = true;
|
|
913
|
-
|
|
949
|
+
terminalStatusOverride = "error";
|
|
914
950
|
return;
|
|
915
951
|
}
|
|
916
952
|
if (event.event.type === "turn_completed") {
|
|
917
|
-
currentStatus = "idle";
|
|
918
953
|
hasStarted = true;
|
|
919
|
-
finish(null);
|
|
920
954
|
}
|
|
921
955
|
if (event.event.type === "turn_canceled") {
|
|
922
|
-
currentStatus = "idle";
|
|
923
956
|
hasStarted = true;
|
|
924
|
-
finish(null);
|
|
925
957
|
}
|
|
926
958
|
}
|
|
927
959
|
}, { agentId, replayState: true });
|
|
@@ -932,6 +964,7 @@ export class AgentManager {
|
|
|
932
964
|
if (this.agents.has(resolvedAgentId)) {
|
|
933
965
|
throw new Error(`Agent with id ${resolvedAgentId} already exists`);
|
|
934
966
|
}
|
|
967
|
+
const initialPersistedTitle = await this.resolveInitialPersistedTitle(resolvedAgentId, config);
|
|
935
968
|
const now = new Date();
|
|
936
969
|
const initialTimeline = options?.timeline ? [...options.timeline] : [];
|
|
937
970
|
const initialTimelineRows = options?.timelineRows?.length
|
|
@@ -982,15 +1015,26 @@ export class AgentManager {
|
|
|
982
1015
|
this.previousStatuses.set(resolvedAgentId, managed.lifecycle);
|
|
983
1016
|
await this.refreshRuntimeInfo(managed);
|
|
984
1017
|
await this.persistSnapshot(managed, {
|
|
985
|
-
title:
|
|
1018
|
+
title: initialPersistedTitle,
|
|
986
1019
|
});
|
|
987
1020
|
this.emitState(managed);
|
|
988
1021
|
await this.refreshSessionState(managed);
|
|
989
1022
|
managed.lifecycle = "idle";
|
|
990
1023
|
await this.persistSnapshot(managed);
|
|
991
1024
|
this.emitState(managed);
|
|
1025
|
+
this.startLiveEventPump(managed);
|
|
992
1026
|
return { ...managed };
|
|
993
1027
|
}
|
|
1028
|
+
async resolveInitialPersistedTitle(agentId, config) {
|
|
1029
|
+
const existing = await this.registry?.get(agentId);
|
|
1030
|
+
if (existing) {
|
|
1031
|
+
return existing.title ?? null;
|
|
1032
|
+
}
|
|
1033
|
+
if (Object.prototype.hasOwnProperty.call(config, "title")) {
|
|
1034
|
+
return config.title ?? null;
|
|
1035
|
+
}
|
|
1036
|
+
return null;
|
|
1037
|
+
}
|
|
994
1038
|
buildTimelineRowsFromItems(items, startSeq, timestamp) {
|
|
995
1039
|
let nextSeq = startSeq;
|
|
996
1040
|
return items.map((item) => {
|
|
@@ -1053,6 +1097,7 @@ export class AgentManager {
|
|
|
1053
1097
|
try {
|
|
1054
1098
|
const newInfo = await agent.session.getRuntimeInfo();
|
|
1055
1099
|
const changed = newInfo.model !== agent.runtimeInfo?.model ||
|
|
1100
|
+
newInfo.thinkingOptionId !== agent.runtimeInfo?.thinkingOptionId ||
|
|
1056
1101
|
newInfo.sessionId !== agent.runtimeInfo?.sessionId ||
|
|
1057
1102
|
newInfo.modeId !== agent.runtimeInfo?.modeId;
|
|
1058
1103
|
agent.runtimeInfo = newInfo;
|
|
@@ -1073,9 +1118,22 @@ export class AgentManager {
|
|
|
1073
1118
|
return;
|
|
1074
1119
|
}
|
|
1075
1120
|
agent.historyPrimed = true;
|
|
1121
|
+
const canonicalUserMessagesById = new Map(agent.timelineRows.flatMap((row) => {
|
|
1122
|
+
if (row.item.type !== "user_message") {
|
|
1123
|
+
return [];
|
|
1124
|
+
}
|
|
1125
|
+
const messageId = normalizeMessageId(row.item.messageId);
|
|
1126
|
+
if (!messageId) {
|
|
1127
|
+
return [];
|
|
1128
|
+
}
|
|
1129
|
+
return [[messageId, row.item.text]];
|
|
1130
|
+
}));
|
|
1076
1131
|
try {
|
|
1077
1132
|
for await (const event of agent.session.streamHistory()) {
|
|
1078
|
-
this.handleStreamEvent(agent, event, {
|
|
1133
|
+
this.handleStreamEvent(agent, event, {
|
|
1134
|
+
fromHistory: true,
|
|
1135
|
+
canonicalUserMessagesById: canonicalUserMessagesById.size > 0 ? canonicalUserMessagesById : undefined,
|
|
1136
|
+
});
|
|
1079
1137
|
}
|
|
1080
1138
|
}
|
|
1081
1139
|
catch {
|
|
@@ -1104,6 +1162,23 @@ export class AgentManager {
|
|
|
1104
1162
|
}
|
|
1105
1163
|
break;
|
|
1106
1164
|
case "timeline":
|
|
1165
|
+
// Skip provider-replayed user_message items during history hydration.
|
|
1166
|
+
// These are already canonically recorded by recordUserMessage() and replaying them would
|
|
1167
|
+
// create duplicates. Match by messageId (not text) to avoid dropping legitimate
|
|
1168
|
+
// provider-origin messages that happen to reuse the same text.
|
|
1169
|
+
if (options?.fromHistory &&
|
|
1170
|
+
event.item.type === "user_message") {
|
|
1171
|
+
const eventMessageId = normalizeMessageId(event.item.messageId);
|
|
1172
|
+
if (eventMessageId) {
|
|
1173
|
+
const canonicalText = options?.canonicalUserMessagesById?.get(eventMessageId);
|
|
1174
|
+
if (canonicalText === event.item.text) {
|
|
1175
|
+
break;
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
if (this.shouldSuppressLiveUserMessageEcho(agent, event, options)) {
|
|
1180
|
+
break;
|
|
1181
|
+
}
|
|
1107
1182
|
timelineRow = this.recordTimeline(agent, event.item);
|
|
1108
1183
|
if (!options?.fromHistory &&
|
|
1109
1184
|
event.item.type === "user_message") {
|
|
@@ -1114,9 +1189,16 @@ export class AgentManager {
|
|
|
1114
1189
|
case "turn_completed":
|
|
1115
1190
|
agent.lastUsage = event.usage;
|
|
1116
1191
|
agent.lastError = undefined;
|
|
1192
|
+
if (!agent.pendingRun && agent.lifecycle !== "idle") {
|
|
1193
|
+
agent.lifecycle = "idle";
|
|
1194
|
+
this.emitState(agent);
|
|
1195
|
+
}
|
|
1117
1196
|
void this.refreshRuntimeInfo(agent);
|
|
1118
1197
|
break;
|
|
1119
1198
|
case "turn_failed":
|
|
1199
|
+
if (!agent.pendingRun) {
|
|
1200
|
+
agent.lifecycle = "error";
|
|
1201
|
+
}
|
|
1120
1202
|
agent.lastError = event.error;
|
|
1121
1203
|
for (const [requestId] of agent.pendingPermissions) {
|
|
1122
1204
|
agent.pendingPermissions.delete(requestId);
|
|
@@ -1132,6 +1214,9 @@ export class AgentManager {
|
|
|
1132
1214
|
this.emitState(agent);
|
|
1133
1215
|
break;
|
|
1134
1216
|
case "turn_canceled":
|
|
1217
|
+
if (!agent.pendingRun) {
|
|
1218
|
+
agent.lifecycle = "idle";
|
|
1219
|
+
}
|
|
1135
1220
|
agent.lastError = undefined;
|
|
1136
1221
|
for (const [requestId] of agent.pendingPermissions) {
|
|
1137
1222
|
agent.pendingPermissions.delete(requestId);
|
|
@@ -1146,6 +1231,12 @@ export class AgentManager {
|
|
|
1146
1231
|
}
|
|
1147
1232
|
this.emitState(agent);
|
|
1148
1233
|
break;
|
|
1234
|
+
case "turn_started":
|
|
1235
|
+
if (!agent.pendingRun) {
|
|
1236
|
+
agent.lifecycle = "running";
|
|
1237
|
+
this.emitState(agent);
|
|
1238
|
+
}
|
|
1239
|
+
break;
|
|
1149
1240
|
case "permission_requested":
|
|
1150
1241
|
agent.pendingPermissions.set(event.request.id, event.request);
|
|
1151
1242
|
this.emitState(agent);
|
|
@@ -1167,6 +1258,27 @@ export class AgentManager {
|
|
|
1167
1258
|
: undefined);
|
|
1168
1259
|
}
|
|
1169
1260
|
}
|
|
1261
|
+
shouldSuppressLiveUserMessageEcho(agent, event, options) {
|
|
1262
|
+
if (options?.fromHistory || event.type !== "timeline") {
|
|
1263
|
+
return false;
|
|
1264
|
+
}
|
|
1265
|
+
if (event.item.type !== "user_message" || !agent.pendingRun) {
|
|
1266
|
+
return false;
|
|
1267
|
+
}
|
|
1268
|
+
const eventMessageId = normalizeMessageId(event.item.messageId);
|
|
1269
|
+
const eventText = event.item.text;
|
|
1270
|
+
if (!eventMessageId) {
|
|
1271
|
+
return false;
|
|
1272
|
+
}
|
|
1273
|
+
return agent.timelineRows.some((row) => {
|
|
1274
|
+
const rowItem = row.item;
|
|
1275
|
+
if (rowItem.type !== "user_message") {
|
|
1276
|
+
return false;
|
|
1277
|
+
}
|
|
1278
|
+
const rowMessageId = normalizeMessageId(rowItem.messageId);
|
|
1279
|
+
return rowMessageId === eventMessageId && rowItem.text === eventText;
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1170
1282
|
recordTimeline(agent, item) {
|
|
1171
1283
|
const timelineState = this.ensureTimelineState(agent);
|
|
1172
1284
|
const row = {
|
|
@@ -1343,5 +1455,136 @@ export class AgentManager {
|
|
|
1343
1455
|
}
|
|
1344
1456
|
return agent;
|
|
1345
1457
|
}
|
|
1458
|
+
startLiveEventPump(agent) {
|
|
1459
|
+
if (!supportsLiveEventStream(agent.session)) {
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
if (this.liveEventPumps.has(agent.id)) {
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
const pump = (async () => {
|
|
1466
|
+
while (true) {
|
|
1467
|
+
const current = this.agents.get(agent.id);
|
|
1468
|
+
if (!current) {
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
if (!supportsLiveEventStream(current.session)) {
|
|
1472
|
+
return;
|
|
1473
|
+
}
|
|
1474
|
+
try {
|
|
1475
|
+
for await (const event of current.session.streamLiveEvents()) {
|
|
1476
|
+
const latest = this.agents.get(agent.id);
|
|
1477
|
+
if (!latest) {
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
// Keep consuming provider events even during an active foreground run,
|
|
1481
|
+
// then replay them immediately once that run settles.
|
|
1482
|
+
if (latest.pendingRun) {
|
|
1483
|
+
this.enqueueLiveEvent(latest.id, event);
|
|
1484
|
+
continue;
|
|
1485
|
+
}
|
|
1486
|
+
this.flushLiveEventBacklog(latest);
|
|
1487
|
+
this.handleStreamEvent(latest, event);
|
|
1488
|
+
}
|
|
1489
|
+
this.logger.warn({ agentId: agent.id }, "Live event pump stream ended; restarting");
|
|
1490
|
+
}
|
|
1491
|
+
catch (error) {
|
|
1492
|
+
this.logger.warn({ err: error, agentId: agent.id }, "Live event pump failed");
|
|
1493
|
+
}
|
|
1494
|
+
// Keep pump alive unless the agent is gone.
|
|
1495
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
1496
|
+
const latest = this.agents.get(agent.id);
|
|
1497
|
+
if (!latest) {
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
if (!latest.pendingRun) {
|
|
1501
|
+
this.flushLiveEventBacklog(latest);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
})();
|
|
1505
|
+
this.liveEventPumps.set(agent.id, pump);
|
|
1506
|
+
pump.finally(() => {
|
|
1507
|
+
const current = this.liveEventPumps.get(agent.id);
|
|
1508
|
+
if (current === pump) {
|
|
1509
|
+
this.liveEventPumps.delete(agent.id);
|
|
1510
|
+
}
|
|
1511
|
+
});
|
|
1512
|
+
}
|
|
1513
|
+
enqueueLiveEvent(agentId, event) {
|
|
1514
|
+
const existing = this.liveEventBacklog.get(agentId);
|
|
1515
|
+
if (existing) {
|
|
1516
|
+
existing.push(event);
|
|
1517
|
+
return;
|
|
1518
|
+
}
|
|
1519
|
+
this.liveEventBacklog.set(agentId, [event]);
|
|
1520
|
+
}
|
|
1521
|
+
clearLiveEventBacklogFlushTimer(agentId) {
|
|
1522
|
+
const timer = this.liveEventBacklogFlushTimers.get(agentId);
|
|
1523
|
+
if (!timer) {
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
clearTimeout(timer);
|
|
1527
|
+
this.liveEventBacklogFlushTimers.delete(agentId);
|
|
1528
|
+
}
|
|
1529
|
+
scheduleLiveEventBacklogFlush(agentId, delayMs) {
|
|
1530
|
+
if (this.liveEventBacklogFlushTimers.has(agentId)) {
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
const timer = setTimeout(() => {
|
|
1534
|
+
this.liveEventBacklogFlushTimers.delete(agentId);
|
|
1535
|
+
const latest = this.agents.get(agentId);
|
|
1536
|
+
if (!latest) {
|
|
1537
|
+
return;
|
|
1538
|
+
}
|
|
1539
|
+
if (latest.pendingRun) {
|
|
1540
|
+
this.scheduleLiveEventBacklogFlush(agentId, LIVE_BACKLOG_TERMINAL_REPLAY_DELAY_MS);
|
|
1541
|
+
return;
|
|
1542
|
+
}
|
|
1543
|
+
this.flushLiveEventBacklog(latest);
|
|
1544
|
+
}, delayMs);
|
|
1545
|
+
this.liveEventBacklogFlushTimers.set(agentId, timer);
|
|
1546
|
+
}
|
|
1547
|
+
flushLiveEventBacklog(agent) {
|
|
1548
|
+
if (agent.pendingRun) {
|
|
1549
|
+
return;
|
|
1550
|
+
}
|
|
1551
|
+
const pending = this.liveEventBacklog.get(agent.id);
|
|
1552
|
+
if (!pending || pending.length === 0) {
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
this.clearLiveEventBacklogFlushTimer(agent.id);
|
|
1556
|
+
this.liveEventBacklog.delete(agent.id);
|
|
1557
|
+
const immediate = [];
|
|
1558
|
+
const deferred = [];
|
|
1559
|
+
let sawTurnStarted = false;
|
|
1560
|
+
let deferRemainder = false;
|
|
1561
|
+
for (const event of pending) {
|
|
1562
|
+
if (!deferRemainder && sawTurnStarted && isTurnTerminalEvent(event)) {
|
|
1563
|
+
deferRemainder = true;
|
|
1564
|
+
}
|
|
1565
|
+
if (deferRemainder) {
|
|
1566
|
+
deferred.push(event);
|
|
1567
|
+
continue;
|
|
1568
|
+
}
|
|
1569
|
+
immediate.push(event);
|
|
1570
|
+
if (event.type === "turn_started") {
|
|
1571
|
+
sawTurnStarted = true;
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
for (const event of immediate) {
|
|
1575
|
+
this.handleStreamEvent(agent, event);
|
|
1576
|
+
}
|
|
1577
|
+
if (deferred.length === 0) {
|
|
1578
|
+
return;
|
|
1579
|
+
}
|
|
1580
|
+
const existing = this.liveEventBacklog.get(agent.id);
|
|
1581
|
+
if (existing && existing.length > 0) {
|
|
1582
|
+
this.liveEventBacklog.set(agent.id, [...deferred, ...existing]);
|
|
1583
|
+
}
|
|
1584
|
+
else {
|
|
1585
|
+
this.liveEventBacklog.set(agent.id, deferred);
|
|
1586
|
+
}
|
|
1587
|
+
this.scheduleLiveEventBacklogFlush(agent.id, LIVE_BACKLOG_TERMINAL_REPLAY_DELAY_MS);
|
|
1588
|
+
}
|
|
1346
1589
|
}
|
|
1347
1590
|
//# sourceMappingURL=agent-manager.js.map
|