@tiflis-io/tiflis-code-workstation 0.3.28 → 0.3.29
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/main.js +108 -34
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -5113,9 +5113,10 @@ var MessageBroadcasterImpl = class {
|
|
|
5113
5113
|
}
|
|
5114
5114
|
/**
|
|
5115
5115
|
* Broadcasts a message to all clients subscribed to a session (by session ID string).
|
|
5116
|
-
* Sends targeted messages to each subscriber
|
|
5116
|
+
* Sends targeted messages to each subscriber in parallel with timeout.
|
|
5117
|
+
* Prevents slow clients from blocking others.
|
|
5117
5118
|
*/
|
|
5118
|
-
broadcastToSubscribers(sessionId, message) {
|
|
5119
|
+
async broadcastToSubscribers(sessionId, message) {
|
|
5119
5120
|
const session = new SessionId(sessionId);
|
|
5120
5121
|
const subscribers = this.deps.clientRegistry.getSubscribers(session);
|
|
5121
5122
|
const authenticatedSubscribers = subscribers.filter(
|
|
@@ -5124,9 +5125,44 @@ var MessageBroadcasterImpl = class {
|
|
|
5124
5125
|
if (authenticatedSubscribers.length === 0) {
|
|
5125
5126
|
return;
|
|
5126
5127
|
}
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5128
|
+
const SEND_TIMEOUT_MS = 2e3;
|
|
5129
|
+
const sendPromises = authenticatedSubscribers.map(async (client) => {
|
|
5130
|
+
try {
|
|
5131
|
+
await Promise.race([
|
|
5132
|
+
new Promise((resolve2, reject) => {
|
|
5133
|
+
const sent = this.deps.tunnelClient.sendToDevice(
|
|
5134
|
+
client.deviceId.value,
|
|
5135
|
+
message
|
|
5136
|
+
);
|
|
5137
|
+
if (sent) {
|
|
5138
|
+
resolve2();
|
|
5139
|
+
} else {
|
|
5140
|
+
reject(new Error(`sendToDevice returned false for ${client.deviceId.value}`));
|
|
5141
|
+
}
|
|
5142
|
+
}),
|
|
5143
|
+
new Promise(
|
|
5144
|
+
(_, reject) => setTimeout(
|
|
5145
|
+
() => reject(new Error(`Send timeout for ${client.deviceId.value}`)),
|
|
5146
|
+
SEND_TIMEOUT_MS
|
|
5147
|
+
)
|
|
5148
|
+
)
|
|
5149
|
+
]);
|
|
5150
|
+
this.logger.debug(
|
|
5151
|
+
{ deviceId: client.deviceId.value, sessionId },
|
|
5152
|
+
"Message sent to subscriber"
|
|
5153
|
+
);
|
|
5154
|
+
} catch (error) {
|
|
5155
|
+
this.logger.warn(
|
|
5156
|
+
{
|
|
5157
|
+
deviceId: client.deviceId.value,
|
|
5158
|
+
sessionId,
|
|
5159
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5160
|
+
},
|
|
5161
|
+
"Failed to send to subscriber (timeout or error)"
|
|
5162
|
+
);
|
|
5163
|
+
}
|
|
5164
|
+
});
|
|
5165
|
+
await Promise.allSettled(sendPromises);
|
|
5130
5166
|
}
|
|
5131
5167
|
};
|
|
5132
5168
|
|
|
@@ -10132,7 +10168,7 @@ async function bootstrap() {
|
|
|
10132
10168
|
message_id: messageId
|
|
10133
10169
|
}
|
|
10134
10170
|
};
|
|
10135
|
-
messageBroadcaster.broadcastToSubscribers(
|
|
10171
|
+
await messageBroadcaster.broadcastToSubscribers(
|
|
10136
10172
|
sessionId,
|
|
10137
10173
|
JSON.stringify(errorEvent)
|
|
10138
10174
|
);
|
|
@@ -10168,7 +10204,7 @@ async function bootstrap() {
|
|
|
10168
10204
|
from_device_id: deviceId
|
|
10169
10205
|
}
|
|
10170
10206
|
};
|
|
10171
|
-
messageBroadcaster.broadcastToSubscribers(
|
|
10207
|
+
await messageBroadcaster.broadcastToSubscribers(
|
|
10172
10208
|
sessionId,
|
|
10173
10209
|
JSON.stringify(transcriptionEvent)
|
|
10174
10210
|
);
|
|
@@ -10259,7 +10295,7 @@ async function bootstrap() {
|
|
|
10259
10295
|
timestamp: Date.now()
|
|
10260
10296
|
}
|
|
10261
10297
|
};
|
|
10262
|
-
messageBroadcaster.broadcastToSubscribers(
|
|
10298
|
+
await messageBroadcaster.broadcastToSubscribers(
|
|
10263
10299
|
sessionId,
|
|
10264
10300
|
JSON.stringify(errorEvent)
|
|
10265
10301
|
);
|
|
@@ -10387,14 +10423,20 @@ async function bootstrap() {
|
|
|
10387
10423
|
is_complete: true
|
|
10388
10424
|
}
|
|
10389
10425
|
};
|
|
10390
|
-
messageBroadcaster.broadcastToSubscribers(
|
|
10426
|
+
void messageBroadcaster.broadcastToSubscribers(
|
|
10391
10427
|
sessionId,
|
|
10392
10428
|
JSON.stringify(cancelOutput)
|
|
10393
|
-
)
|
|
10394
|
-
|
|
10395
|
-
|
|
10396
|
-
|
|
10397
|
-
|
|
10429
|
+
).then(() => {
|
|
10430
|
+
logger.info(
|
|
10431
|
+
{ sessionId },
|
|
10432
|
+
"Broadcasted cancel message to subscribers"
|
|
10433
|
+
);
|
|
10434
|
+
}).catch((error) => {
|
|
10435
|
+
logger.error(
|
|
10436
|
+
{ sessionId, error },
|
|
10437
|
+
"Failed to broadcast cancel message"
|
|
10438
|
+
);
|
|
10439
|
+
});
|
|
10398
10440
|
chatHistoryService.saveAgentMessage(sessionId, "assistant", "", [
|
|
10399
10441
|
cancelBlock
|
|
10400
10442
|
]);
|
|
@@ -10880,9 +10922,13 @@ async function bootstrap() {
|
|
|
10880
10922
|
subscriptionRepository,
|
|
10881
10923
|
logger
|
|
10882
10924
|
});
|
|
10925
|
+
let supervisorMessageSequence = 0;
|
|
10926
|
+
const agentMessageSequences = /* @__PURE__ */ new Map();
|
|
10883
10927
|
const broadcaster = messageBroadcaster;
|
|
10884
10928
|
const agentMessageAccumulator = /* @__PURE__ */ new Map();
|
|
10885
10929
|
const agentStreamingMessageIds = /* @__PURE__ */ new Map();
|
|
10930
|
+
const STREAMING_STATE_GRACE_PERIOD_MS = 1e4;
|
|
10931
|
+
const agentCleanupTimeouts = /* @__PURE__ */ new Map();
|
|
10886
10932
|
const getOrCreateAgentStreamingMessageId = (sessionId) => {
|
|
10887
10933
|
let messageId = agentStreamingMessageIds.get(sessionId);
|
|
10888
10934
|
if (!messageId) {
|
|
@@ -10892,7 +10938,16 @@ async function bootstrap() {
|
|
|
10892
10938
|
return messageId;
|
|
10893
10939
|
};
|
|
10894
10940
|
const clearAgentStreamingMessageId = (sessionId) => {
|
|
10895
|
-
|
|
10941
|
+
const existingTimeout = agentCleanupTimeouts.get(sessionId);
|
|
10942
|
+
if (existingTimeout) {
|
|
10943
|
+
clearTimeout(existingTimeout);
|
|
10944
|
+
}
|
|
10945
|
+
const timeout = setTimeout(() => {
|
|
10946
|
+
agentStreamingMessageIds.delete(sessionId);
|
|
10947
|
+
agentCleanupTimeouts.delete(sessionId);
|
|
10948
|
+
logger.debug({ sessionId }, "Agent streaming state cleaned up after grace period");
|
|
10949
|
+
}, STREAMING_STATE_GRACE_PERIOD_MS);
|
|
10950
|
+
agentCleanupTimeouts.set(sessionId, timeout);
|
|
10896
10951
|
};
|
|
10897
10952
|
agentSessionManager.on(
|
|
10898
10953
|
"blocks",
|
|
@@ -10966,10 +11021,14 @@ async function bootstrap() {
|
|
|
10966
11021
|
}
|
|
10967
11022
|
const mergedBlocks = mergeToolBlocks(accumulatedBlocks);
|
|
10968
11023
|
const fullAccumulatedText = mergedBlocks.filter((b) => b.block_type === "text").map((b) => b.content).join("\n");
|
|
11024
|
+
const currentSequence = agentMessageSequences.get(sessionId) ?? 0;
|
|
11025
|
+
const newSequence = currentSequence + 1;
|
|
11026
|
+
agentMessageSequences.set(sessionId, newSequence);
|
|
10969
11027
|
const outputEvent = {
|
|
10970
11028
|
type: "session.output",
|
|
10971
11029
|
session_id: sessionId,
|
|
10972
11030
|
streaming_message_id: streamingMessageId,
|
|
11031
|
+
sequence: newSequence,
|
|
10973
11032
|
payload: {
|
|
10974
11033
|
content_type: "agent",
|
|
10975
11034
|
content: fullAccumulatedText,
|
|
@@ -10980,13 +11039,19 @@ async function bootstrap() {
|
|
|
10980
11039
|
is_complete: isComplete
|
|
10981
11040
|
}
|
|
10982
11041
|
};
|
|
10983
|
-
broadcaster.broadcastToSubscribers(
|
|
11042
|
+
await broadcaster.broadcastToSubscribers(
|
|
10984
11043
|
sessionId,
|
|
10985
11044
|
JSON.stringify(outputEvent)
|
|
10986
11045
|
);
|
|
10987
11046
|
if (isComplete) {
|
|
10988
11047
|
clearAgentStreamingMessageId(sessionId);
|
|
10989
|
-
|
|
11048
|
+
setTimeout(() => {
|
|
11049
|
+
agentMessageAccumulator.delete(sessionId);
|
|
11050
|
+
logger.debug(
|
|
11051
|
+
{ sessionId, messageId: streamingMessageId },
|
|
11052
|
+
"Agent message accumulator cleaned up after grace period"
|
|
11053
|
+
);
|
|
11054
|
+
}, STREAMING_STATE_GRACE_PERIOD_MS);
|
|
10990
11055
|
}
|
|
10991
11056
|
if (isComplete && fullTextContent.length > 0) {
|
|
10992
11057
|
const pendingVoiceCommand = pendingAgentVoiceCommands.get(sessionId);
|
|
@@ -11045,7 +11110,7 @@ async function bootstrap() {
|
|
|
11045
11110
|
from_device_id: originDeviceId
|
|
11046
11111
|
}
|
|
11047
11112
|
};
|
|
11048
|
-
broadcaster.broadcastToSubscribers(
|
|
11113
|
+
await broadcaster.broadcastToSubscribers(
|
|
11049
11114
|
sessionId,
|
|
11050
11115
|
JSON.stringify(voiceOutputEvent)
|
|
11051
11116
|
);
|
|
@@ -11090,6 +11155,7 @@ async function bootstrap() {
|
|
|
11090
11155
|
const outputEvent = {
|
|
11091
11156
|
type: "supervisor.output",
|
|
11092
11157
|
streaming_message_id: streamingMessageId,
|
|
11158
|
+
sequence: ++supervisorMessageSequence,
|
|
11093
11159
|
payload: {
|
|
11094
11160
|
content_type: "supervisor",
|
|
11095
11161
|
content: textContent,
|
|
@@ -11101,7 +11167,13 @@ async function bootstrap() {
|
|
|
11101
11167
|
const message = JSON.stringify(outputEvent);
|
|
11102
11168
|
broadcaster.broadcastToAll(message);
|
|
11103
11169
|
if (isComplete) {
|
|
11104
|
-
|
|
11170
|
+
setTimeout(() => {
|
|
11171
|
+
supervisorMessageAccumulator.clear();
|
|
11172
|
+
logger.debug(
|
|
11173
|
+
{ messageId: streamingMessageId },
|
|
11174
|
+
"Supervisor message accumulator cleaned up after grace period"
|
|
11175
|
+
);
|
|
11176
|
+
}, STREAMING_STATE_GRACE_PERIOD_MS);
|
|
11105
11177
|
}
|
|
11106
11178
|
if (isComplete && finalOutput && finalOutput.length > 0) {
|
|
11107
11179
|
chatHistoryService.saveSupervisorMessage(
|
|
@@ -11230,21 +11302,23 @@ async function bootstrap() {
|
|
|
11230
11302
|
batchIntervalMs: env.TERMINAL_BATCH_INTERVAL_MS,
|
|
11231
11303
|
maxBatchSize: env.TERMINAL_BATCH_MAX_SIZE,
|
|
11232
11304
|
onFlush: (batchedData) => {
|
|
11233
|
-
|
|
11234
|
-
|
|
11235
|
-
|
|
11236
|
-
|
|
11237
|
-
|
|
11238
|
-
|
|
11239
|
-
|
|
11240
|
-
|
|
11241
|
-
|
|
11242
|
-
|
|
11243
|
-
|
|
11244
|
-
|
|
11245
|
-
|
|
11246
|
-
|
|
11247
|
-
|
|
11305
|
+
void (async () => {
|
|
11306
|
+
const outputMessage = session.addOutputToBuffer(batchedData);
|
|
11307
|
+
const outputEvent = {
|
|
11308
|
+
type: "session.output",
|
|
11309
|
+
session_id: sessionId.value,
|
|
11310
|
+
payload: {
|
|
11311
|
+
content_type: "terminal",
|
|
11312
|
+
content: batchedData,
|
|
11313
|
+
timestamp: outputMessage.timestamp,
|
|
11314
|
+
sequence: outputMessage.sequence
|
|
11315
|
+
}
|
|
11316
|
+
};
|
|
11317
|
+
await broadcaster.broadcastToSubscribers(
|
|
11318
|
+
sessionId.value,
|
|
11319
|
+
JSON.stringify(outputEvent)
|
|
11320
|
+
);
|
|
11321
|
+
})();
|
|
11248
11322
|
}
|
|
11249
11323
|
});
|
|
11250
11324
|
session.onOutput((data) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiflis-io/tiflis-code-workstation",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.29",
|
|
4
4
|
"description": "Workstation server for tiflis-code - manages agent sessions and terminal access",
|
|
5
5
|
"author": "Roman Barinov <rbarinov@gmail.com>",
|
|
6
6
|
"license": "FSL-1.1-NC",
|