@tiflis-io/tiflis-code-workstation 0.3.12 → 0.3.13

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/dist/main.js +116 -26
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -127,7 +127,11 @@ var EnvSchema = z.object({
127
127
  // Terminal Configuration
128
128
  // ─────────────────────────────────────────────────────────────
129
129
  /** Terminal output buffer size (number of messages, in-memory only, does not survive restarts) */
130
- TERMINAL_OUTPUT_BUFFER_SIZE: z.coerce.number().default(100),
130
+ TERMINAL_OUTPUT_BUFFER_SIZE: z.coerce.number().default(1e4),
131
+ /** Terminal output batch interval in milliseconds (how long to wait before flushing) */
132
+ TERMINAL_BATCH_INTERVAL_MS: z.coerce.number().default(64),
133
+ /** Terminal output batch max size in bytes (flush immediately when exceeded) */
134
+ TERMINAL_BATCH_MAX_SIZE: z.coerce.number().default(4096),
131
135
  // Legacy (fallback for STT/TTS if specific keys not set)
132
136
  OPENAI_API_KEY: z.string().optional(),
133
137
  // ─────────────────────────────────────────────────────────────
@@ -283,7 +287,7 @@ var SESSION_CONFIG = {
283
287
  /** Data retention period for terminated sessions (30 days in ms) */
284
288
  DATA_RETENTION_MS: 30 * 24 * 60 * 60 * 1e3,
285
289
  /** Default terminal output buffer size (number of messages) */
286
- DEFAULT_TERMINAL_OUTPUT_BUFFER_SIZE: 100
290
+ DEFAULT_TERMINAL_OUTPUT_BUFFER_SIZE: 1e4
287
291
  };
288
292
  var AGENT_COMMANDS = {
289
293
  cursor: {
@@ -2588,15 +2592,12 @@ var TerminalSession = class extends Session {
2588
2592
  return this._masterDeviceId;
2589
2593
  }
2590
2594
  /**
2591
- * Sets the master device ID. Only sets if not already set (first subscriber wins).
2592
- * @returns true if this device became master, false if master was already set
2595
+ * Sets the master device ID. Always overrides the current master (new subscriber wins).
2596
+ * @returns true (new subscriber always becomes master)
2593
2597
  */
2594
2598
  setMaster(deviceId) {
2595
- if (this._masterDeviceId === null) {
2596
- this._masterDeviceId = deviceId;
2597
- return true;
2598
- }
2599
- return this._masterDeviceId === deviceId;
2599
+ this._masterDeviceId = deviceId;
2600
+ return true;
2600
2601
  }
2601
2602
  /**
2602
2603
  * Checks if the given device is the master for this session.
@@ -2917,6 +2918,75 @@ var PtyManager = class {
2917
2918
  }
2918
2919
  };
2919
2920
 
2921
+ // src/infrastructure/terminal/terminal-output-batcher.ts
2922
+ var TerminalOutputBatcher = class {
2923
+ buffer = "";
2924
+ timeout = null;
2925
+ lastActivityTime = Date.now();
2926
+ outputRate = 0;
2927
+ // Bytes/second estimate (exponential moving average)
2928
+ batchIntervalMs;
2929
+ maxBatchSize;
2930
+ onFlush;
2931
+ constructor(config2) {
2932
+ this.batchIntervalMs = config2.batchIntervalMs;
2933
+ this.maxBatchSize = config2.maxBatchSize;
2934
+ this.onFlush = config2.onFlush;
2935
+ }
2936
+ /**
2937
+ * Appends data to the batch buffer.
2938
+ * Triggers flush based on size threshold or adaptive timeout.
2939
+ */
2940
+ append(chunk) {
2941
+ this.buffer += chunk;
2942
+ const now = Date.now();
2943
+ const elapsed = Math.max(now - this.lastActivityTime, 1);
2944
+ const instantRate = chunk.length / elapsed * 1e3;
2945
+ this.outputRate = this.outputRate * 0.7 + instantRate * 0.3;
2946
+ this.lastActivityTime = now;
2947
+ if (this.buffer.length >= this.maxBatchSize) {
2948
+ this.flush();
2949
+ return;
2950
+ }
2951
+ if (this.timeout === null) {
2952
+ const adaptiveInterval = this.outputRate > 1e3 ? this.batchIntervalMs : Math.min(8, this.batchIntervalMs);
2953
+ this.timeout = setTimeout(() => this.flush(), adaptiveInterval);
2954
+ }
2955
+ }
2956
+ /**
2957
+ * Immediately flushes the buffer, invoking the onFlush callback.
2958
+ */
2959
+ flush() {
2960
+ if (this.timeout !== null) {
2961
+ clearTimeout(this.timeout);
2962
+ this.timeout = null;
2963
+ }
2964
+ if (this.buffer.length > 0) {
2965
+ const data = this.buffer;
2966
+ this.buffer = "";
2967
+ this.onFlush(data);
2968
+ }
2969
+ }
2970
+ /**
2971
+ * Disposes the batcher, flushing any pending data.
2972
+ */
2973
+ dispose() {
2974
+ this.flush();
2975
+ }
2976
+ /**
2977
+ * Returns current buffer size (for debugging/monitoring).
2978
+ */
2979
+ get pendingSize() {
2980
+ return this.buffer.length;
2981
+ }
2982
+ /**
2983
+ * Returns estimated output rate in bytes/second.
2984
+ */
2985
+ get currentOutputRate() {
2986
+ return this.outputRate;
2987
+ }
2988
+ };
2989
+
2920
2990
  // src/infrastructure/agents/agent-session-manager.ts
2921
2991
  import { EventEmitter as EventEmitter2 } from "events";
2922
2992
  import { randomUUID as randomUUID2 } from "crypto";
@@ -9461,7 +9531,7 @@ async function bootstrap() {
9461
9531
  let currentStreamingBlocks;
9462
9532
  if (supervisorIsExecuting) {
9463
9533
  const blocks = supervisorMessageAccumulator.get();
9464
- if (blocks && blocks.length > 0) {
9534
+ if (blocks.length > 0) {
9465
9535
  currentStreamingBlocks = blocks;
9466
9536
  }
9467
9537
  }
@@ -10540,7 +10610,7 @@ async function bootstrap() {
10540
10610
  let streamingMessageId;
10541
10611
  if (isExecuting && !beforeSequence) {
10542
10612
  const blocks = supervisorMessageAccumulator.get();
10543
- if (blocks && blocks.length > 0) {
10613
+ if (blocks.length > 0) {
10544
10614
  currentStreamingBlocks = blocks;
10545
10615
  streamingMessageId = supervisorMessageAccumulator.getStreamingMessageId() ?? void 0;
10546
10616
  }
@@ -11153,23 +11223,35 @@ async function bootstrap() {
11153
11223
  }
11154
11224
  };
11155
11225
  broadcaster.broadcastToAll(JSON.stringify(broadcastMessage));
11226
+ const batcher = new TerminalOutputBatcher({
11227
+ batchIntervalMs: env.TERMINAL_BATCH_INTERVAL_MS,
11228
+ maxBatchSize: env.TERMINAL_BATCH_MAX_SIZE,
11229
+ onFlush: (batchedData) => {
11230
+ const outputMessage = session.addOutputToBuffer(batchedData);
11231
+ const outputEvent = {
11232
+ type: "session.output",
11233
+ session_id: sessionId.value,
11234
+ payload: {
11235
+ content_type: "terminal",
11236
+ content: batchedData,
11237
+ timestamp: outputMessage.timestamp,
11238
+ sequence: outputMessage.sequence
11239
+ }
11240
+ };
11241
+ broadcaster.broadcastToSubscribers(
11242
+ sessionId.value,
11243
+ JSON.stringify(outputEvent)
11244
+ );
11245
+ }
11246
+ });
11156
11247
  session.onOutput((data) => {
11157
- const outputMessage = session.addOutputToBuffer(data);
11158
- const outputEvent = {
11159
- type: "session.output",
11160
- session_id: sessionId.value,
11161
- payload: {
11162
- content_type: "terminal",
11163
- content: data,
11164
- timestamp: outputMessage.timestamp,
11165
- sequence: outputMessage.sequence
11166
- }
11167
- };
11168
- broadcaster.broadcastToSubscribers(
11169
- sessionId.value,
11170
- JSON.stringify(outputEvent)
11171
- );
11248
+ batcher.append(data);
11172
11249
  });
11250
+ const originalTerminate = session.terminate.bind(session);
11251
+ session.terminate = async () => {
11252
+ batcher.dispose();
11253
+ return originalTerminate();
11254
+ };
11173
11255
  });
11174
11256
  if (env.MOCK_MODE) {
11175
11257
  const terminalSession = await sessionManager.createSession({
@@ -11401,6 +11483,14 @@ bootstrap().catch((error) => {
11401
11483
  * @copyright 2025 Roman Barinov <rbarinov@gmail.com>
11402
11484
  * @license FSL-1.1-NC
11403
11485
  */
11486
+ /**
11487
+ * @file terminal-output-batcher.ts
11488
+ * @copyright 2025 Roman Barinov <rbarinov@gmail.com>
11489
+ * @license FSL-1.1-NC
11490
+ *
11491
+ * Batches terminal output chunks to reduce message frequency and improve performance.
11492
+ * Uses adaptive batching based on output rate for optimal responsiveness.
11493
+ */
11404
11494
  /**
11405
11495
  * @file headless-agent-executor.ts
11406
11496
  * @copyright 2025 Roman Barinov <rbarinov@gmail.com>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiflis-io/tiflis-code-workstation",
3
- "version": "0.3.12",
3
+ "version": "0.3.13",
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",