remcodex 0.1.0-beta.8 → 0.1.0-beta.9

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.
@@ -9,6 +9,7 @@ const node_os_1 = __importDefault(require("node:os"));
9
9
  const node_path_1 = __importDefault(require("node:path"));
10
10
  const errors_1 = require("../utils/errors");
11
11
  const ids_1 = require("../utils/ids");
12
+ const output_limits_1 = require("../utils/output-limits");
12
13
  function computeSourceRolloutHasOpenTurnFromRecords(records) {
13
14
  const openTurnIds = new Set();
14
15
  for (const record of records) {
@@ -571,22 +572,7 @@ function translateRolloutRecords(records, emitFromRecordIndex = 0) {
571
572
  : `call_${index}`;
572
573
  const started = commandStarts.get(callId) || null;
573
574
  const parsed = parseExecOutput(payload.output);
574
- if (parsed.outputText) {
575
- appendSemantic(index, {
576
- type: "command.output.delta",
577
- turnId: currentTurnId,
578
- messageId: null,
579
- callId,
580
- requestId: null,
581
- phase: null,
582
- stream: "stdout",
583
- payload: {
584
- stream: "stdout",
585
- textDelta: parsed.outputText,
586
- },
587
- timestamp,
588
- });
589
- }
575
+ const cappedOutput = parsed.outputText ? (0, output_limits_1.capTextValue)(parsed.outputText) : null;
590
576
  appendSemantic(index, {
591
577
  type: "command.end",
592
578
  turnId: currentTurnId,
@@ -598,6 +584,9 @@ function translateRolloutRecords(records, emitFromRecordIndex = 0) {
598
584
  payload: {
599
585
  command: parsed.commandLine || started?.commandPayload.command || null,
600
586
  cwd: started?.commandPayload.cwd || null,
587
+ stdout: cappedOutput?.text || null,
588
+ aggregatedOutput: cappedOutput?.text || null,
589
+ stdoutTruncated: cappedOutput?.truncated || undefined,
601
590
  status: parsed.exitCode == null
602
591
  ? "completed"
603
592
  : parsed.exitCode === 0
@@ -55,9 +55,24 @@ class EventStore {
55
55
  `)
56
56
  .get(id);
57
57
  const event = this.toPayload(row);
58
- this.captureLatestQuota(sessionId, event);
59
- this.emitter.emit(this.channel(sessionId), event);
60
- return event;
58
+ return this.publish(event);
59
+ }
60
+ publishTransient(sessionId, input, seq) {
61
+ const event = {
62
+ id: input.id?.trim() || (0, ids_1.createId)("evt"),
63
+ sessionId,
64
+ type: input.type,
65
+ seq,
66
+ timestamp: input.timestamp?.trim() || new Date().toISOString(),
67
+ turnId: input.turnId ?? null,
68
+ messageId: input.messageId ?? null,
69
+ callId: input.callId ?? null,
70
+ requestId: input.requestId ?? null,
71
+ phase: input.phase ?? null,
72
+ stream: this.normalizeStream(input.stream),
73
+ payload: input.payload ?? {},
74
+ };
75
+ return this.publish(event);
61
76
  }
62
77
  list(sessionId, options = {}) {
63
78
  const safeLimit = Math.max(1, Math.min(options.limit ?? 200, 200));
@@ -241,7 +256,7 @@ class EventStore {
241
256
  this.emitter.off(channel, listener);
242
257
  };
243
258
  }
244
- nextSeq(sessionId) {
259
+ latestSeq(sessionId) {
245
260
  const row = this.db
246
261
  .prepare(`
247
262
  SELECT COALESCE(MAX(seq), 0) AS current_seq
@@ -249,7 +264,15 @@ class EventStore {
249
264
  WHERE session_id = ?
250
265
  `)
251
266
  .get(sessionId);
252
- return row.current_seq + 1;
267
+ return row.current_seq;
268
+ }
269
+ nextSeq(sessionId) {
270
+ return this.latestSeq(sessionId) + 1;
271
+ }
272
+ publish(event) {
273
+ this.captureLatestQuota(event.sessionId, event);
274
+ this.emitter.emit(this.channel(event.sessionId), event);
275
+ return event;
253
276
  }
254
277
  toPayload(row) {
255
278
  return {
@@ -9,11 +9,13 @@ const node_os_1 = __importDefault(require("node:os"));
9
9
  const node_path_1 = __importDefault(require("node:path"));
10
10
  const errors_1 = require("../utils/errors");
11
11
  const ids_1 = require("../utils/ids");
12
+ const output_limits_1 = require("../utils/output-limits");
12
13
  const codex_runner_1 = require("./codex-runner");
13
14
  const codex_stream_events_1 = require("./codex-stream-events");
14
15
  function nowIso() {
15
16
  return new Date().toISOString();
16
17
  }
18
+ const TRANSIENT_SEQ_STEP = 0.00001;
17
19
  function shouldAutotitleSession(title) {
18
20
  const normalized = String(title || "").trim();
19
21
  return (!normalized ||
@@ -346,6 +348,7 @@ class SessionManager {
346
348
  const runtime = {
347
349
  runner,
348
350
  stopRequested: false,
351
+ transientSeqCursor: this.options.eventStore.latestSeq(sessionId),
349
352
  turnId,
350
353
  appTurnId: null,
351
354
  turnStarted: false,
@@ -945,6 +948,8 @@ class SessionManager {
945
948
  cwd: payload.cwd || null,
946
949
  stdout: "",
947
950
  stderr: "",
951
+ stdoutTruncated: false,
952
+ stderrTruncated: false,
948
953
  started: true,
949
954
  completed: false,
950
955
  });
@@ -971,14 +976,15 @@ class SessionManager {
971
976
  if (!current) {
972
977
  return;
973
978
  }
974
- if (stream === "stderr") {
975
- current.stderr += textDelta;
976
- }
977
- else {
978
- current.stdout += textDelta;
979
+ const targetKey = stream === "stderr" ? "stderr" : "stdout";
980
+ const truncatedKey = stream === "stderr" ? "stderrTruncated" : "stdoutTruncated";
981
+ const capped = (0, output_limits_1.appendCappedText)(current[targetKey], textDelta);
982
+ current[targetKey] = capped.nextText;
983
+ if (capped.truncated) {
984
+ current[truncatedKey] = true;
979
985
  }
980
986
  runtime.activeCommandCallId = callId;
981
- this.appendEvent(sessionId, {
987
+ this.publishTransientEvent(sessionId, runtime, {
982
988
  type: "command.output.delta",
983
989
  turnId: runtime.turnId,
984
990
  messageId: null,
@@ -1013,6 +1019,11 @@ class SessionManager {
1013
1019
  payload: {
1014
1020
  command: payload.command || current.command,
1015
1021
  cwd: payload.cwd || current.cwd,
1022
+ stdout: current.stdout || null,
1023
+ stderr: current.stderr || null,
1024
+ aggregatedOutput: current.stdout || current.stderr || null,
1025
+ stdoutTruncated: current.stdoutTruncated || undefined,
1026
+ stderrTruncated: current.stderrTruncated || undefined,
1016
1027
  status: payload.status || (payload.exitCode === 0 ? "completed" : "failed"),
1017
1028
  exitCode: payload.exitCode ?? null,
1018
1029
  durationMs: payload.durationMs ?? null,
@@ -1218,9 +1229,18 @@ class SessionManager {
1218
1229
  }
1219
1230
  appendEvent(sessionId, input) {
1220
1231
  const event = this.options.eventStore.append(sessionId, input);
1232
+ const runtime = this.runners.get(sessionId);
1233
+ if (runtime) {
1234
+ runtime.transientSeqCursor = Math.max(runtime.transientSeqCursor, Number(event.seq || 0));
1235
+ }
1221
1236
  this.touchSession(sessionId);
1222
1237
  return event;
1223
1238
  }
1239
+ publishTransientEvent(sessionId, runtime, input) {
1240
+ runtime.transientSeqCursor =
1241
+ Math.round((runtime.transientSeqCursor + TRANSIENT_SEQ_STEP) * 100000) / 100000;
1242
+ return this.options.eventStore.publishTransient(sessionId, input, runtime.transientSeqCursor);
1243
+ }
1224
1244
  touchSession(sessionId) {
1225
1245
  this.options.db
1226
1246
  .prepare(`
@@ -1,181 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SessionTimelineService = void 0;
4
- const DEFAULT_TIMELINE_LIMIT = 200;
5
- const MAX_TIMELINE_LIMIT = 400;
6
- function clampLimit(limit) {
7
- const numeric = Number(limit || DEFAULT_TIMELINE_LIMIT);
8
- if (!Number.isFinite(numeric) || numeric <= 0) {
9
- return DEFAULT_TIMELINE_LIMIT;
10
- }
11
- return Math.max(1, Math.min(Math.trunc(numeric), MAX_TIMELINE_LIMIT));
12
- }
13
- function normalizeCursor(value) {
14
- const numeric = Number(value || 0);
15
- if (!Number.isFinite(numeric) || numeric <= 0) {
16
- return 0;
17
- }
18
- return Math.trunc(numeric);
19
- }
20
- function cloneEvent(event) {
21
- return {
22
- ...event,
23
- payload: event.payload && typeof event.payload === "object"
24
- ? { ...event.payload }
25
- : event.payload,
26
- };
27
- }
28
- function appendTextDelta(currentValue, nextValue) {
29
- if (!nextValue) {
30
- return currentValue || "";
31
- }
32
- return `${currentValue || ""}${nextValue}`;
33
- }
34
- function compareEvents(left, right) {
35
- if (left.seq !== right.seq) {
36
- return left.seq - right.seq;
37
- }
38
- return String(left.id || "").localeCompare(String(right.id || ""));
39
- }
40
- function upsertTimelineEvent(items, indexById, nextEvent) {
41
- const existing = indexById.get(nextEvent.id);
42
- if (existing) {
43
- Object.assign(existing, nextEvent);
44
- return existing;
45
- }
46
- const cloned = cloneEvent(nextEvent);
47
- items.push(cloned);
48
- indexById.set(cloned.id, cloned);
49
- return cloned;
50
- }
51
- function timelineAssistantDeltaId(event) {
52
- return `timeline:assistant:delta:${event.messageId || event.id}`;
53
- }
54
- function timelineReasoningDeltaId(event) {
55
- return `timeline:reasoning:delta:${event.messageId || event.id}`;
56
- }
57
- function timelineCommandOutputId(event) {
58
- return `timeline:command:output:${event.callId || event.id}:${event.stream || "stdout"}`;
59
- }
60
- function timelinePatchOutputId(event) {
61
- return `timeline:patch:output:${event.callId || event.id}`;
62
- }
63
- function aggregateSemanticTimeline(rawEvents) {
64
- const items = [];
65
- const indexById = new Map();
66
- rawEvents.forEach((event) => {
67
- switch (event.type) {
68
- case "message.assistant.delta": {
69
- const syntheticId = timelineAssistantDeltaId(event);
70
- const existing = indexById.get(syntheticId);
71
- upsertTimelineEvent(items, indexById, {
72
- ...cloneEvent(event),
73
- id: syntheticId,
74
- payload: {
75
- ...(event.payload || {}),
76
- textDelta: appendTextDelta(existing?.payload?.textDelta || "", String(event.payload?.textDelta || "")),
77
- },
78
- });
79
- break;
80
- }
81
- case "reasoning.delta": {
82
- const syntheticId = timelineReasoningDeltaId(event);
83
- const existing = indexById.get(syntheticId);
84
- const nextText = appendTextDelta(existing?.payload?.textDelta || "", String(event.payload?.textDelta || ""));
85
- upsertTimelineEvent(items, indexById, {
86
- ...cloneEvent(event),
87
- id: syntheticId,
88
- payload: {
89
- ...(event.payload || {}),
90
- textDelta: nextText,
91
- summary: event.payload?.summary ||
92
- existing?.payload?.summary ||
93
- nextText ||
94
- null,
95
- },
96
- });
97
- break;
98
- }
99
- case "command.output.delta": {
100
- const syntheticId = timelineCommandOutputId(event);
101
- const existing = indexById.get(syntheticId);
102
- upsertTimelineEvent(items, indexById, {
103
- ...cloneEvent(event),
104
- id: syntheticId,
105
- payload: {
106
- ...(event.payload || {}),
107
- textDelta: appendTextDelta(existing?.payload?.textDelta || "", String(event.payload?.textDelta || "")),
108
- stream: event.payload?.stream || event.stream || "stdout",
109
- },
110
- });
111
- break;
112
- }
113
- case "patch.output.delta": {
114
- const syntheticId = timelinePatchOutputId(event);
115
- const existing = indexById.get(syntheticId);
116
- upsertTimelineEvent(items, indexById, {
117
- ...cloneEvent(event),
118
- id: syntheticId,
119
- payload: {
120
- ...(event.payload || {}),
121
- textDelta: appendTextDelta(existing?.payload?.textDelta || "", String(event.payload?.textDelta || "")),
122
- },
123
- });
124
- break;
125
- }
126
- default:
127
- upsertTimelineEvent(items, indexById, event);
128
- break;
129
- }
130
- });
131
- return items.sort(compareEvents);
132
- }
133
- function paginateTimelineItems(items, options, lastSeq) {
134
- const limit = clampLimit(options.limit);
135
- const after = normalizeCursor(options.after);
136
- const before = normalizeCursor(options.before);
137
- if (before > 0) {
138
- const matches = items.filter((item) => item.seq < before);
139
- const hasMoreBefore = matches.length > limit;
140
- const pageItems = matches.slice(Math.max(0, matches.length - limit));
141
- return {
142
- items: pageItems,
143
- nextCursor: pageItems.at(-1)?.seq || after,
144
- beforeCursor: pageItems[0]?.seq || before,
145
- hasMoreBefore,
146
- lastSeq,
147
- };
148
- }
149
- if (after > 0) {
150
- const pageItems = items.filter((item) => item.seq > after).slice(0, limit);
151
- return {
152
- items: pageItems,
153
- nextCursor: pageItems.at(-1)?.seq || after,
154
- beforeCursor: pageItems[0]?.seq || 0,
155
- hasMoreBefore: pageItems.length > 0 ? pageItems[0].seq > 1 : items.length > 0,
156
- lastSeq,
157
- };
158
- }
159
- const hasMoreBefore = items.length > limit;
160
- const pageItems = items.slice(Math.max(0, items.length - limit));
161
- return {
162
- items: pageItems,
163
- nextCursor: pageItems.at(-1)?.seq || 0,
164
- beforeCursor: pageItems[0]?.seq || 0,
165
- hasMoreBefore,
166
- lastSeq,
167
- };
168
- }
169
4
  class SessionTimelineService {
170
5
  eventStore;
171
6
  constructor(eventStore) {
172
7
  this.eventStore = eventStore;
173
8
  }
174
9
  list(sessionId, options = {}) {
175
- const rawEvents = this.eventStore.listAll(sessionId);
176
- const lastSeq = rawEvents.at(-1)?.seq || 0;
177
- const aggregatedItems = aggregateSemanticTimeline(rawEvents);
178
- return paginateTimelineItems(aggregatedItems, options, lastSeq);
10
+ const page = this.eventStore.list(sessionId, options);
11
+ return {
12
+ ...page,
13
+ // The initial detail load only needs the latest observed seq so resume sync
14
+ // can continue from the newest page we fetched.
15
+ lastSeq: page.nextCursor || Math.max(0, Number(options.after || 0)),
16
+ };
179
17
  }
180
18
  }
181
19
  exports.SessionTimelineService = SessionTimelineService;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.COMMAND_STREAM_TRUNCATION_NOTICE = exports.MAX_PERSISTED_COMMAND_STREAM_CHARS = void 0;
4
+ exports.appendCappedText = appendCappedText;
5
+ exports.capTextValue = capTextValue;
6
+ exports.MAX_PERSISTED_COMMAND_STREAM_CHARS = 80 * 1024;
7
+ exports.COMMAND_STREAM_TRUNCATION_NOTICE = "\n\n[command output truncated]\n";
8
+ function normalizeMaxChars(maxChars, notice) {
9
+ const numeric = Number(maxChars || exports.MAX_PERSISTED_COMMAND_STREAM_CHARS);
10
+ if (!Number.isFinite(numeric) || numeric <= notice.length + 1) {
11
+ return exports.MAX_PERSISTED_COMMAND_STREAM_CHARS;
12
+ }
13
+ return Math.trunc(numeric);
14
+ }
15
+ function appendCappedText(currentText, nextDelta, options = {}) {
16
+ const notice = String(options.notice || exports.COMMAND_STREAM_TRUNCATION_NOTICE);
17
+ const safeCurrent = String(currentText || "");
18
+ const safeDelta = String(nextDelta || "");
19
+ if (!safeDelta) {
20
+ return {
21
+ nextText: safeCurrent,
22
+ appendedText: "",
23
+ truncated: false,
24
+ };
25
+ }
26
+ const maxChars = normalizeMaxChars(options.maxChars, notice);
27
+ const contentLimit = Math.max(0, maxChars - notice.length);
28
+ if (safeCurrent.endsWith(notice) || safeCurrent.length >= maxChars) {
29
+ return {
30
+ nextText: safeCurrent.length > maxChars ? safeCurrent.slice(0, maxChars) : safeCurrent,
31
+ appendedText: "",
32
+ truncated: true,
33
+ };
34
+ }
35
+ if (safeCurrent.length >= contentLimit) {
36
+ return {
37
+ nextText: `${safeCurrent.slice(0, contentLimit)}${notice}`,
38
+ appendedText: notice,
39
+ truncated: true,
40
+ };
41
+ }
42
+ if (safeCurrent.length + safeDelta.length <= contentLimit) {
43
+ return {
44
+ nextText: safeCurrent + safeDelta,
45
+ appendedText: safeDelta,
46
+ truncated: false,
47
+ };
48
+ }
49
+ const available = Math.max(0, contentLimit - safeCurrent.length);
50
+ const preserved = safeDelta.slice(0, available);
51
+ const appendedText = `${preserved}${notice}`;
52
+ return {
53
+ nextText: `${safeCurrent}${appendedText}`,
54
+ appendedText,
55
+ truncated: true,
56
+ };
57
+ }
58
+ function capTextValue(text, options = {}) {
59
+ const notice = String(options.notice || exports.COMMAND_STREAM_TRUNCATION_NOTICE);
60
+ const safeText = String(text || "");
61
+ const maxChars = normalizeMaxChars(options.maxChars, notice);
62
+ const contentLimit = Math.max(0, maxChars - notice.length);
63
+ if (safeText.length <= contentLimit) {
64
+ return {
65
+ text: safeText,
66
+ truncated: false,
67
+ };
68
+ }
69
+ return {
70
+ text: `${safeText.slice(0, contentLimit)}${notice}`,
71
+ truncated: true,
72
+ };
73
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remcodex",
3
- "version": "0.1.0-beta.8",
3
+ "version": "0.1.0-beta.9",
4
4
  "description": "Control Codex from anywhere. Even on your phone.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -8,11 +8,35 @@ const TURN_STATUS_PRIORITY = {
8
8
  failed: 3,
9
9
  aborted: 4,
10
10
  };
11
+ const MAX_COMMAND_OUTPUT_CHARS = 80 * 1024;
12
+ const MAX_PATCH_OUTPUT_CHARS = 48 * 1024;
13
+ const OUTPUT_TRUNCATION_NOTICE = "\n\n[output truncated]\n";
11
14
 
12
15
  function createItemIndex() {
13
16
  return new Map();
14
17
  }
15
18
 
19
+ function clampOutputText(text, maxChars = MAX_COMMAND_OUTPUT_CHARS) {
20
+ const safeText = String(text || "");
21
+ if (!safeText) {
22
+ return "";
23
+ }
24
+ if (safeText.endsWith(OUTPUT_TRUNCATION_NOTICE)) {
25
+ return safeText;
26
+ }
27
+
28
+ const contentLimit = Math.max(0, maxChars - OUTPUT_TRUNCATION_NOTICE.length);
29
+ if (safeText.length <= contentLimit) {
30
+ return safeText;
31
+ }
32
+
33
+ return `${safeText.slice(0, contentLimit)}${OUTPUT_TRUNCATION_NOTICE}`;
34
+ }
35
+
36
+ function appendClampedOutput(currentText, textDelta, maxChars = MAX_COMMAND_OUTPUT_CHARS) {
37
+ return clampOutputText(`${String(currentText || "")}${String(textDelta || "")}`, maxChars);
38
+ }
39
+
16
40
  function nextTurnFallbackId(event) {
17
41
  return event?.turnId || `turn:${event?.id || crypto.randomUUID?.() || Date.now()}`;
18
42
  }
@@ -526,10 +550,18 @@ export function reduceTimeline(state, event) {
526
550
  const nextStdout =
527
551
  event.payload?.stream === "stderr"
528
552
  ? currentCommand?.stdout || ""
529
- : appendDeltaText(currentCommand?.stdout, event.payload?.textDelta);
553
+ : appendClampedOutput(
554
+ currentCommand?.stdout,
555
+ event.payload?.textDelta,
556
+ MAX_COMMAND_OUTPUT_CHARS,
557
+ );
530
558
  const nextStderr =
531
559
  event.payload?.stream === "stderr"
532
- ? appendDeltaText(currentCommand?.stderr, event.payload?.textDelta)
560
+ ? appendClampedOutput(
561
+ currentCommand?.stderr,
562
+ event.payload?.textDelta,
563
+ MAX_COMMAND_OUTPUT_CHARS,
564
+ )
533
565
  : currentCommand?.stderr || "";
534
566
  upsertCommand(state, event, turnId, {
535
567
  status: currentCommand?.status === "awaiting_approval" ? "awaiting_approval" : "running",
@@ -552,14 +584,22 @@ export function reduceTimeline(state, event) {
552
584
  status: completedStatus,
553
585
  command: event.payload?.command || state.commandsByCallId[event.callId]?.command || "",
554
586
  cwd: event.payload?.cwd || state.commandsByCallId[event.callId]?.cwd || null,
555
- stdout: event.payload?.stdout || state.commandsByCallId[event.callId]?.stdout || "",
556
- stderr: event.payload?.stderr || state.commandsByCallId[event.callId]?.stderr || "",
557
- output:
587
+ stdout: clampOutputText(
588
+ event.payload?.stdout || state.commandsByCallId[event.callId]?.stdout || "",
589
+ MAX_COMMAND_OUTPUT_CHARS,
590
+ ),
591
+ stderr: clampOutputText(
592
+ event.payload?.stderr || state.commandsByCallId[event.callId]?.stderr || "",
593
+ MAX_COMMAND_OUTPUT_CHARS,
594
+ ),
595
+ output: clampOutputText(
558
596
  event.payload?.aggregatedOutput ||
559
- event.payload?.formattedOutput ||
560
- event.payload?.output ||
561
- state.commandsByCallId[event.callId]?.output ||
562
- "",
597
+ event.payload?.formattedOutput ||
598
+ event.payload?.output ||
599
+ state.commandsByCallId[event.callId]?.output ||
600
+ "",
601
+ MAX_COMMAND_OUTPUT_CHARS,
602
+ ),
563
603
  exitCode:
564
604
  event.payload?.exitCode ?? state.commandsByCallId[event.callId]?.exitCode ?? null,
565
605
  duration: event.payload?.duration || state.commandsByCallId[event.callId]?.duration || null,
@@ -583,7 +623,11 @@ export function reduceTimeline(state, event) {
583
623
  const currentPatch = state.patchesByCallId[patchId];
584
624
  upsertPatch(state, event, turnId, {
585
625
  status: currentPatch?.status || "running",
586
- output: appendDeltaText(currentPatch?.output, event.payload?.textDelta),
626
+ output: appendClampedOutput(
627
+ currentPatch?.output,
628
+ event.payload?.textDelta,
629
+ MAX_PATCH_OUTPUT_CHARS,
630
+ ),
587
631
  outputStatus: "streaming",
588
632
  });
589
633
  setTurnStatus(turn, "running");
@@ -598,9 +642,18 @@ export function reduceTimeline(state, event) {
598
642
  status: patchStatus,
599
643
  patchText:
600
644
  event.payload?.patchText || state.patchesByCallId[event.callId]?.patchText || "",
601
- output: event.payload?.output || state.patchesByCallId[event.callId]?.output || "",
602
- stdout: event.payload?.stdout || state.patchesByCallId[event.callId]?.stdout || "",
603
- stderr: event.payload?.stderr || state.patchesByCallId[event.callId]?.stderr || "",
645
+ output: clampOutputText(
646
+ event.payload?.output || state.patchesByCallId[event.callId]?.output || "",
647
+ MAX_PATCH_OUTPUT_CHARS,
648
+ ),
649
+ stdout: clampOutputText(
650
+ event.payload?.stdout || state.patchesByCallId[event.callId]?.stdout || "",
651
+ MAX_PATCH_OUTPUT_CHARS,
652
+ ),
653
+ stderr: clampOutputText(
654
+ event.payload?.stderr || state.patchesByCallId[event.callId]?.stderr || "",
655
+ MAX_PATCH_OUTPUT_CHARS,
656
+ ),
604
657
  changes: event.payload?.changes || state.patchesByCallId[event.callId]?.changes || {},
605
658
  success:
606
659
  event.payload?.success ?? state.patchesByCallId[event.callId]?.success ?? null,