tmux-team 3.2.2 → 3.2.3
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/package.json +1 -1
- package/src/cli.test.ts +1 -1
- package/src/commands/basic-commands.test.ts +1 -1
- package/src/commands/config-command.test.ts +1 -1
- package/src/commands/config.ts +1 -0
- package/src/commands/install.test.ts +1 -1
- package/src/commands/setup.test.ts +1 -1
- package/src/commands/talk.test.ts +25 -6
- package/src/commands/talk.ts +145 -74
- package/src/config.ts +2 -1
- package/src/context.test.ts +1 -1
- package/src/types.ts +1 -0
package/package.json
CHANGED
package/src/cli.test.ts
CHANGED
|
@@ -16,7 +16,7 @@ function makeStubContext(): Context {
|
|
|
16
16
|
config: {
|
|
17
17
|
mode: 'polling',
|
|
18
18
|
preambleMode: 'always',
|
|
19
|
-
defaults: { timeout: 180, pollInterval: 1, captureLines: 100, preambleEvery: 3 },
|
|
19
|
+
defaults: { timeout: 180, pollInterval: 1, captureLines: 100, maxCaptureLines: 2000, preambleEvery: 3 },
|
|
20
20
|
agents: {},
|
|
21
21
|
paneRegistry: {},
|
|
22
22
|
},
|
|
@@ -53,7 +53,7 @@ function createCtx(
|
|
|
53
53
|
const baseConfig: ResolvedConfig = {
|
|
54
54
|
mode: 'polling',
|
|
55
55
|
preambleMode: 'always',
|
|
56
|
-
defaults: { timeout: 180, pollInterval: 1, captureLines: 100, preambleEvery: 3 },
|
|
56
|
+
defaults: { timeout: 180, pollInterval: 1, captureLines: 100, maxCaptureLines: 2000, preambleEvery: 3 },
|
|
57
57
|
agents: {},
|
|
58
58
|
paneRegistry: {},
|
|
59
59
|
...overrides?.config,
|
|
@@ -34,7 +34,7 @@ function createCtx(
|
|
|
34
34
|
const config: ResolvedConfig = {
|
|
35
35
|
mode: 'polling',
|
|
36
36
|
preambleMode: 'always',
|
|
37
|
-
defaults: { timeout: 180, pollInterval: 1, captureLines: 100, preambleEvery: 3 },
|
|
37
|
+
defaults: { timeout: 180, pollInterval: 1, captureLines: 100, maxCaptureLines: 2000, preambleEvery: 3 },
|
|
38
38
|
agents: {},
|
|
39
39
|
paneRegistry: {},
|
|
40
40
|
...configOverrides,
|
package/src/commands/config.ts
CHANGED
|
@@ -26,7 +26,7 @@ function createCtx(testDir: string, overrides?: Partial<{ flags: Partial<Flags>
|
|
|
26
26
|
const config: ResolvedConfig = {
|
|
27
27
|
mode: 'polling',
|
|
28
28
|
preambleMode: 'always',
|
|
29
|
-
defaults: { timeout: 180, pollInterval: 1, captureLines: 100, preambleEvery: 3 },
|
|
29
|
+
defaults: { timeout: 180, pollInterval: 1, captureLines: 100, maxCaptureLines: 2000, preambleEvery: 3 },
|
|
30
30
|
agents: {},
|
|
31
31
|
paneRegistry: {},
|
|
32
32
|
};
|
|
@@ -26,7 +26,7 @@ function createCtx(testDir: string, tmux: Tmux, flags?: Partial<Flags>): Context
|
|
|
26
26
|
const config: ResolvedConfig = {
|
|
27
27
|
mode: 'polling',
|
|
28
28
|
preambleMode: 'always',
|
|
29
|
-
defaults: { timeout: 180, pollInterval: 1, captureLines: 100, preambleEvery: 3 },
|
|
29
|
+
defaults: { timeout: 180, pollInterval: 1, captureLines: 100, maxCaptureLines: 2000, preambleEvery: 3 },
|
|
30
30
|
agents: {},
|
|
31
31
|
paneRegistry: {},
|
|
32
32
|
};
|
|
@@ -79,6 +79,7 @@ function createDefaultConfig(): ResolvedConfig {
|
|
|
79
79
|
timeout: 60,
|
|
80
80
|
pollInterval: 0.1, // Fast polling for tests
|
|
81
81
|
captureLines: 100,
|
|
82
|
+
maxCaptureLines: 2000,
|
|
82
83
|
preambleEvery: 3,
|
|
83
84
|
},
|
|
84
85
|
agents: {},
|
|
@@ -236,6 +237,7 @@ describe('buildMessage (via cmdTalk)', () => {
|
|
|
236
237
|
timeout: 60,
|
|
237
238
|
pollInterval: 0.1,
|
|
238
239
|
captureLines: 100,
|
|
240
|
+
maxCaptureLines: 2000,
|
|
239
241
|
preambleEvery: 3,
|
|
240
242
|
},
|
|
241
243
|
};
|
|
@@ -272,6 +274,7 @@ describe('buildMessage (via cmdTalk)', () => {
|
|
|
272
274
|
timeout: 60,
|
|
273
275
|
pollInterval: 0.1,
|
|
274
276
|
captureLines: 100,
|
|
277
|
+
maxCaptureLines: 2000,
|
|
275
278
|
preambleEvery: 1,
|
|
276
279
|
},
|
|
277
280
|
};
|
|
@@ -295,6 +298,7 @@ describe('buildMessage (via cmdTalk)', () => {
|
|
|
295
298
|
timeout: 60,
|
|
296
299
|
pollInterval: 0.1,
|
|
297
300
|
captureLines: 100,
|
|
301
|
+
maxCaptureLines: 2000,
|
|
298
302
|
preambleEvery: 0,
|
|
299
303
|
},
|
|
300
304
|
};
|
|
@@ -491,6 +495,7 @@ describe('cmdTalk - --wait mode', () => {
|
|
|
491
495
|
timeout: 5,
|
|
492
496
|
pollInterval: 0.01,
|
|
493
497
|
captureLines: 100,
|
|
498
|
+
maxCaptureLines: 2000,
|
|
494
499
|
preambleEvery: 3,
|
|
495
500
|
},
|
|
496
501
|
},
|
|
@@ -534,6 +539,7 @@ describe('cmdTalk - --wait mode', () => {
|
|
|
534
539
|
timeout: 5,
|
|
535
540
|
pollInterval: 0.01,
|
|
536
541
|
captureLines: 100,
|
|
542
|
+
maxCaptureLines: 2000,
|
|
537
543
|
preambleEvery: 3,
|
|
538
544
|
},
|
|
539
545
|
},
|
|
@@ -564,6 +570,7 @@ describe('cmdTalk - --wait mode', () => {
|
|
|
564
570
|
timeout: 0.1,
|
|
565
571
|
pollInterval: 0.02,
|
|
566
572
|
captureLines: 100,
|
|
573
|
+
maxCaptureLines: 2000,
|
|
567
574
|
preambleEvery: 3,
|
|
568
575
|
},
|
|
569
576
|
},
|
|
@@ -611,6 +618,7 @@ describe('cmdTalk - --wait mode', () => {
|
|
|
611
618
|
timeout: 5,
|
|
612
619
|
pollInterval: 0.01,
|
|
613
620
|
captureLines: 100,
|
|
621
|
+
maxCaptureLines: 2000,
|
|
614
622
|
preambleEvery: 3,
|
|
615
623
|
},
|
|
616
624
|
},
|
|
@@ -646,6 +654,7 @@ describe('cmdTalk - --wait mode', () => {
|
|
|
646
654
|
timeout: 5,
|
|
647
655
|
pollInterval: 0.01,
|
|
648
656
|
captureLines: 100,
|
|
657
|
+
maxCaptureLines: 2000,
|
|
649
658
|
preambleEvery: 3,
|
|
650
659
|
},
|
|
651
660
|
},
|
|
@@ -674,6 +683,7 @@ describe('cmdTalk - --wait mode', () => {
|
|
|
674
683
|
timeout: 0.05,
|
|
675
684
|
pollInterval: 0.01,
|
|
676
685
|
captureLines: 100,
|
|
686
|
+
maxCaptureLines: 2000,
|
|
677
687
|
preambleEvery: 3,
|
|
678
688
|
},
|
|
679
689
|
},
|
|
@@ -728,6 +738,7 @@ describe('cmdTalk - --wait mode', () => {
|
|
|
728
738
|
timeout: 5,
|
|
729
739
|
pollInterval: 0.05,
|
|
730
740
|
captureLines: 100,
|
|
741
|
+
maxCaptureLines: 2000,
|
|
731
742
|
preambleEvery: 3,
|
|
732
743
|
},
|
|
733
744
|
paneRegistry: {
|
|
@@ -775,6 +786,7 @@ describe('cmdTalk - --wait mode', () => {
|
|
|
775
786
|
timeout: 0.5,
|
|
776
787
|
pollInterval: 0.02,
|
|
777
788
|
captureLines: 100,
|
|
789
|
+
maxCaptureLines: 2000,
|
|
778
790
|
preambleEvery: 3,
|
|
779
791
|
},
|
|
780
792
|
paneRegistry: {
|
|
@@ -832,6 +844,7 @@ describe('cmdTalk - --wait mode', () => {
|
|
|
832
844
|
timeout: 5,
|
|
833
845
|
pollInterval: 0.02,
|
|
834
846
|
captureLines: 100,
|
|
847
|
+
maxCaptureLines: 2000,
|
|
835
848
|
preambleEvery: 3,
|
|
836
849
|
},
|
|
837
850
|
paneRegistry: {
|
|
@@ -941,6 +954,7 @@ describe('cmdTalk - nonce collision handling', () => {
|
|
|
941
954
|
timeout: 5,
|
|
942
955
|
pollInterval: 0.01,
|
|
943
956
|
captureLines: 100,
|
|
957
|
+
maxCaptureLines: 2000,
|
|
944
958
|
preambleEvery: 3,
|
|
945
959
|
},
|
|
946
960
|
},
|
|
@@ -995,6 +1009,7 @@ describe('cmdTalk - JSON output contract', () => {
|
|
|
995
1009
|
timeout: 5,
|
|
996
1010
|
pollInterval: 0.01,
|
|
997
1011
|
captureLines: 100,
|
|
1012
|
+
maxCaptureLines: 2000,
|
|
998
1013
|
preambleEvery: 3,
|
|
999
1014
|
},
|
|
1000
1015
|
},
|
|
@@ -1035,6 +1050,7 @@ describe('cmdTalk - JSON output contract', () => {
|
|
|
1035
1050
|
timeout: 0.05,
|
|
1036
1051
|
pollInterval: 0.01,
|
|
1037
1052
|
captureLines: 100,
|
|
1053
|
+
maxCaptureLines: 2000,
|
|
1038
1054
|
preambleEvery: 3,
|
|
1039
1055
|
},
|
|
1040
1056
|
},
|
|
@@ -1076,6 +1092,7 @@ describe('cmdTalk - JSON output contract', () => {
|
|
|
1076
1092
|
timeout: 0.05,
|
|
1077
1093
|
pollInterval: 0.01,
|
|
1078
1094
|
captureLines: 100,
|
|
1095
|
+
maxCaptureLines: 2000,
|
|
1079
1096
|
preambleEvery: 3,
|
|
1080
1097
|
},
|
|
1081
1098
|
},
|
|
@@ -1112,6 +1129,7 @@ describe('cmdTalk - JSON output contract', () => {
|
|
|
1112
1129
|
timeout: 0.05,
|
|
1113
1130
|
pollInterval: 0.01,
|
|
1114
1131
|
captureLines: 100,
|
|
1132
|
+
maxCaptureLines: 2000,
|
|
1115
1133
|
preambleEvery: 3,
|
|
1116
1134
|
},
|
|
1117
1135
|
},
|
|
@@ -1162,6 +1180,7 @@ describe('cmdTalk - JSON output contract', () => {
|
|
|
1162
1180
|
timeout: 0.5,
|
|
1163
1181
|
pollInterval: 0.02,
|
|
1164
1182
|
captureLines: 100,
|
|
1183
|
+
maxCaptureLines: 2000,
|
|
1165
1184
|
preambleEvery: 3,
|
|
1166
1185
|
},
|
|
1167
1186
|
paneRegistry: {
|
|
@@ -1244,7 +1263,7 @@ describe('cmdTalk - end marker detection', () => {
|
|
|
1244
1263
|
ui,
|
|
1245
1264
|
paths: createTestPaths(testDir),
|
|
1246
1265
|
flags: { wait: true, json: true, timeout: 5 },
|
|
1247
|
-
config: { defaults: { timeout: 5, pollInterval: 0.01, captureLines: 100, preambleEvery: 3 } },
|
|
1266
|
+
config: { defaults: { timeout: 5, pollInterval: 0.01, captureLines: 100, maxCaptureLines: 2000, preambleEvery: 3 } },
|
|
1248
1267
|
});
|
|
1249
1268
|
|
|
1250
1269
|
await cmdTalk(ctx, 'claude', 'Test message');
|
|
@@ -1277,7 +1296,7 @@ describe('cmdTalk - end marker detection', () => {
|
|
|
1277
1296
|
ui,
|
|
1278
1297
|
paths: createTestPaths(testDir),
|
|
1279
1298
|
flags: { wait: true, json: true, timeout: 5 },
|
|
1280
|
-
config: { defaults: { timeout: 5, pollInterval: 0.01, captureLines: 100, preambleEvery: 3 } },
|
|
1299
|
+
config: { defaults: { timeout: 5, pollInterval: 0.01, captureLines: 100, maxCaptureLines: 2000, preambleEvery: 3 } },
|
|
1281
1300
|
});
|
|
1282
1301
|
|
|
1283
1302
|
await cmdTalk(ctx, 'claude', 'Test');
|
|
@@ -1311,7 +1330,7 @@ Line 4 final`;
|
|
|
1311
1330
|
ui,
|
|
1312
1331
|
paths: createTestPaths(testDir),
|
|
1313
1332
|
flags: { wait: true, json: true, timeout: 5 },
|
|
1314
|
-
config: { defaults: { timeout: 5, pollInterval: 0.01, captureLines: 100, preambleEvery: 3 } },
|
|
1333
|
+
config: { defaults: { timeout: 5, pollInterval: 0.01, captureLines: 100, maxCaptureLines: 2000, preambleEvery: 3 } },
|
|
1315
1334
|
});
|
|
1316
1335
|
|
|
1317
1336
|
await cmdTalk(ctx, 'claude', 'Test');
|
|
@@ -1342,7 +1361,7 @@ Line 4 final`;
|
|
|
1342
1361
|
ui,
|
|
1343
1362
|
paths: createTestPaths(testDir),
|
|
1344
1363
|
flags: { wait: true, json: true, timeout: 5 },
|
|
1345
|
-
config: { defaults: { timeout: 5, pollInterval: 0.01, captureLines: 100, preambleEvery: 3 } },
|
|
1364
|
+
config: { defaults: { timeout: 5, pollInterval: 0.01, captureLines: 100, maxCaptureLines: 2000, preambleEvery: 3 } },
|
|
1346
1365
|
});
|
|
1347
1366
|
|
|
1348
1367
|
await cmdTalk(ctx, 'claude', 'Test');
|
|
@@ -1379,7 +1398,7 @@ Line 4 final`;
|
|
|
1379
1398
|
ui,
|
|
1380
1399
|
paths: createTestPaths(testDir),
|
|
1381
1400
|
flags: { wait: true, json: true, timeout: 5 },
|
|
1382
|
-
config: { defaults: { timeout: 5, pollInterval: 0.01, captureLines: 100, preambleEvery: 3 } },
|
|
1401
|
+
config: { defaults: { timeout: 5, pollInterval: 0.01, captureLines: 100, maxCaptureLines: 2000, preambleEvery: 3 } },
|
|
1383
1402
|
});
|
|
1384
1403
|
|
|
1385
1404
|
await cmdTalk(ctx, 'claude', 'Test');
|
|
@@ -1415,7 +1434,7 @@ Line 4 final`;
|
|
|
1415
1434
|
ui,
|
|
1416
1435
|
paths: createTestPaths(testDir),
|
|
1417
1436
|
flags: { wait: true, json: true, timeout: 5 },
|
|
1418
|
-
config: { defaults: { timeout: 5, pollInterval: 0.01, captureLines: 200, preambleEvery: 3 } },
|
|
1437
|
+
config: { defaults: { timeout: 5, pollInterval: 0.01, captureLines: 200, maxCaptureLines: 2000, preambleEvery: 3 } },
|
|
1419
1438
|
});
|
|
1420
1439
|
|
|
1421
1440
|
await cmdTalk(ctx, 'claude', 'Test');
|
package/src/commands/talk.ts
CHANGED
|
@@ -118,6 +118,99 @@ function extractPartialResponse(
|
|
|
118
118
|
return partial || null;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
// ─────────────────────────────────────────────────────────────
|
|
122
|
+
// Expandable capture extraction
|
|
123
|
+
// ─────────────────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
interface ExtractResult {
|
|
126
|
+
response: string;
|
|
127
|
+
truncated: boolean;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Extract response with expandable capture.
|
|
132
|
+
*
|
|
133
|
+
* When the instruction line is not found in the initial capture (response too long),
|
|
134
|
+
* retry with progressively larger captures up to maxCaptureLines.
|
|
135
|
+
*/
|
|
136
|
+
function extractWithExpandableCapture(
|
|
137
|
+
tmux: Context['tmux'],
|
|
138
|
+
pane: string,
|
|
139
|
+
nonce: string,
|
|
140
|
+
endMarkerRegex: RegExp,
|
|
141
|
+
initialCapture: string,
|
|
142
|
+
captureLines: number,
|
|
143
|
+
maxCaptureLines: number,
|
|
144
|
+
responseLines: number,
|
|
145
|
+
debug?: boolean
|
|
146
|
+
): ExtractResult {
|
|
147
|
+
let output = initialCapture;
|
|
148
|
+
let currentCaptureLines = captureLines;
|
|
149
|
+
let truncated = false;
|
|
150
|
+
|
|
151
|
+
// Try extraction with progressively larger captures
|
|
152
|
+
while (true) {
|
|
153
|
+
const lines = output.split('\n');
|
|
154
|
+
|
|
155
|
+
// Find end marker line (case-insensitive, last occurrence)
|
|
156
|
+
let endMarkerLineIndex = -1;
|
|
157
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
158
|
+
if (endMarkerRegex.test(lines[i])) {
|
|
159
|
+
endMarkerLineIndex = i;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (endMarkerLineIndex === -1) {
|
|
165
|
+
// No marker found - shouldn't happen if called correctly
|
|
166
|
+
return { response: '', truncated: true };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Find instruction line (contains nonce but is not the marker)
|
|
170
|
+
const instructionLineIndex = lines.findIndex(
|
|
171
|
+
(line) =>
|
|
172
|
+
line.toLowerCase().includes(`response-end-${nonce.toLowerCase()}`) &&
|
|
173
|
+
!endMarkerRegex.test(line)
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
if (instructionLineIndex !== -1 && instructionLineIndex < endMarkerLineIndex) {
|
|
177
|
+
// Instruction visible: extract from after instruction to marker
|
|
178
|
+
const response = lines.slice(instructionLineIndex + 1, endMarkerLineIndex).join('\n').trim();
|
|
179
|
+
return { response, truncated: false };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Instruction not found - try larger capture if possible
|
|
183
|
+
if (currentCaptureLines >= maxCaptureLines) {
|
|
184
|
+
// Already at max, fall back to N lines before marker
|
|
185
|
+
if (debug) {
|
|
186
|
+
console.error(
|
|
187
|
+
`[DEBUG] Instruction line not found after max capture (${maxCaptureLines} lines), falling back`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
truncated = true;
|
|
191
|
+
const startLine = Math.max(0, endMarkerLineIndex - responseLines);
|
|
192
|
+
const response = lines.slice(startLine, endMarkerLineIndex).join('\n').trim();
|
|
193
|
+
return { response, truncated };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Double capture size and retry
|
|
197
|
+
currentCaptureLines = Math.min(currentCaptureLines * 2, maxCaptureLines);
|
|
198
|
+
if (debug) {
|
|
199
|
+
console.error(`[DEBUG] Expanding capture to ${currentCaptureLines} lines`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
output = tmux.capture(pane, currentCaptureLines);
|
|
204
|
+
} catch {
|
|
205
|
+
// Capture failed, use what we have
|
|
206
|
+
truncated = true;
|
|
207
|
+
const startLine = Math.max(0, endMarkerLineIndex - responseLines);
|
|
208
|
+
const response = lines.slice(startLine, endMarkerLineIndex).join('\n').trim();
|
|
209
|
+
return { response, truncated };
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
121
214
|
// ─────────────────────────────────────────────────────────────
|
|
122
215
|
// Types for broadcast wait mode
|
|
123
216
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -131,6 +224,7 @@ interface AgentWaitState {
|
|
|
131
224
|
status: 'pending' | 'completed' | 'timeout' | 'error';
|
|
132
225
|
response?: string;
|
|
133
226
|
partialResponse?: string | null;
|
|
227
|
+
truncated?: boolean;
|
|
134
228
|
error?: string;
|
|
135
229
|
elapsedMs?: number;
|
|
136
230
|
// Per-agent timing
|
|
@@ -140,6 +234,20 @@ interface AgentWaitState {
|
|
|
140
234
|
lastOutputChangeAt: number;
|
|
141
235
|
}
|
|
142
236
|
|
|
237
|
+
interface AgentResultOutput {
|
|
238
|
+
agent: string;
|
|
239
|
+
pane: string;
|
|
240
|
+
requestId: string;
|
|
241
|
+
nonce: string;
|
|
242
|
+
endMarker: string;
|
|
243
|
+
status: 'pending' | 'completed' | 'timeout' | 'error';
|
|
244
|
+
response?: string;
|
|
245
|
+
partialResponse?: string | null;
|
|
246
|
+
truncated?: boolean;
|
|
247
|
+
error?: string;
|
|
248
|
+
elapsedMs?: number;
|
|
249
|
+
}
|
|
250
|
+
|
|
143
251
|
interface BroadcastWaitResult {
|
|
144
252
|
target: 'all';
|
|
145
253
|
mode: 'wait';
|
|
@@ -152,7 +260,7 @@ interface BroadcastWaitResult {
|
|
|
152
260
|
error: number;
|
|
153
261
|
skipped: number;
|
|
154
262
|
};
|
|
155
|
-
results:
|
|
263
|
+
results: AgentResultOutput[];
|
|
156
264
|
}
|
|
157
265
|
|
|
158
266
|
/**
|
|
@@ -483,47 +591,24 @@ export async function cmdTalk(ctx: Context, target: string, message: string): Pr
|
|
|
483
591
|
console.error(`[DEBUG] Agent completed (elapsed: ${elapsedMs}ms, idle: ${idleMs}ms)`);
|
|
484
592
|
}
|
|
485
593
|
|
|
486
|
-
// Extract response
|
|
594
|
+
// Extract response with expandable capture (handles long responses)
|
|
487
595
|
const responseLines = flags.lines ?? 100;
|
|
488
|
-
const
|
|
489
|
-
|
|
490
|
-
// Find the line with the end marker (last occurrence = agent's marker)
|
|
491
|
-
// Find end marker line (case-insensitive)
|
|
492
|
-
let endMarkerLineIndex = -1;
|
|
493
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
494
|
-
if (endMarkerRegex.test(lines[i])) {
|
|
495
|
-
endMarkerLineIndex = i;
|
|
496
|
-
break;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
596
|
+
const maxCaptureLines = config.defaults.maxCaptureLines;
|
|
499
597
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
line.toLowerCase().includes(`response-end-${nonce.toLowerCase()}`) &&
|
|
511
|
-
!endMarkerRegex.test(line)
|
|
598
|
+
const { response: extractedResponse, truncated } = extractWithExpandableCapture(
|
|
599
|
+
tmux,
|
|
600
|
+
pane,
|
|
601
|
+
nonce,
|
|
602
|
+
endMarkerRegex,
|
|
603
|
+
output,
|
|
604
|
+
captureLines,
|
|
605
|
+
maxCaptureLines,
|
|
606
|
+
responseLines,
|
|
607
|
+
flags.debug
|
|
512
608
|
);
|
|
513
609
|
|
|
514
|
-
if (instructionLineIndex !== -1 && instructionLineIndex < endMarkerLineIndex) {
|
|
515
|
-
// Instruction visible: extract from after instruction to marker
|
|
516
|
-
startLine = instructionLineIndex + 1;
|
|
517
|
-
} else {
|
|
518
|
-
// Instruction scrolled off: extract N lines before marker
|
|
519
|
-
startLine = Math.max(0, endMarkerLineIndex - responseLines);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
let response = lines.slice(startLine, endMarkerLineIndex).join('\n').trim();
|
|
523
610
|
// Clean Gemini CLI UI artifacts
|
|
524
|
-
|
|
525
|
-
response = cleanGeminiResponse(response);
|
|
526
|
-
}
|
|
611
|
+
const response = target === 'gemini' ? cleanGeminiResponse(extractedResponse) : extractedResponse;
|
|
527
612
|
|
|
528
613
|
if (!flags.json && isTTY) {
|
|
529
614
|
process.stdout.write('\r' + ' '.repeat(80) + '\r');
|
|
@@ -536,8 +621,11 @@ export async function cmdTalk(ctx: Context, target: string, message: string): Pr
|
|
|
536
621
|
|
|
537
622
|
const result: WaitResult = { requestId, nonce, endMarker, response };
|
|
538
623
|
if (flags.json) {
|
|
539
|
-
ui.json({ target, pane, status: 'completed', ...result });
|
|
624
|
+
ui.json({ target, pane, status: 'completed', truncated, ...result });
|
|
540
625
|
} else {
|
|
626
|
+
if (truncated) {
|
|
627
|
+
ui.warn('Response may be truncated (instruction line not found in scrollback)');
|
|
628
|
+
}
|
|
541
629
|
console.log(colors.cyan(`─── Response from ${target} (${pane}) ───`));
|
|
542
630
|
console.log(response);
|
|
543
631
|
}
|
|
@@ -765,47 +853,26 @@ async function cmdTalkAllWait(
|
|
|
765
853
|
continue;
|
|
766
854
|
}
|
|
767
855
|
|
|
768
|
-
// Extract response
|
|
856
|
+
// Extract response with expandable capture (handles long responses)
|
|
769
857
|
const responseLines = flags.lines ?? 100;
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
// Protocol: instruction describes the marker verbally but doesn't contain the literal string.
|
|
784
|
-
// So any occurrence of the literal marker is definitively from the agent.
|
|
785
|
-
//
|
|
786
|
-
// Try to anchor extraction to the instruction line (cleaner output when visible).
|
|
787
|
-
// Fall back to N lines before marker if instruction scrolled off.
|
|
788
|
-
let startLine: number;
|
|
789
|
-
const instructionLineIndex = lines.findIndex(
|
|
790
|
-
(line) =>
|
|
791
|
-
line.toLowerCase().includes(`response-end-${state.nonce.toLowerCase()}`) &&
|
|
792
|
-
!endMarkerRegex.test(line)
|
|
858
|
+
const maxCaptureLines = config.defaults.maxCaptureLines;
|
|
859
|
+
|
|
860
|
+
const { response: extractedResponse, truncated } = extractWithExpandableCapture(
|
|
861
|
+
tmux,
|
|
862
|
+
state.pane,
|
|
863
|
+
state.nonce,
|
|
864
|
+
endMarkerRegex,
|
|
865
|
+
output,
|
|
866
|
+
captureLines,
|
|
867
|
+
maxCaptureLines,
|
|
868
|
+
responseLines,
|
|
869
|
+
flags.debug
|
|
793
870
|
);
|
|
794
871
|
|
|
795
|
-
if (instructionLineIndex !== -1 && instructionLineIndex < endMarkerLineIndex) {
|
|
796
|
-
// Instruction visible: extract from after instruction to marker
|
|
797
|
-
startLine = instructionLineIndex + 1;
|
|
798
|
-
} else {
|
|
799
|
-
// Instruction scrolled off: extract N lines before marker
|
|
800
|
-
startLine = Math.max(0, endMarkerLineIndex - responseLines);
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
let response = lines.slice(startLine, endMarkerLineIndex).join('\n').trim();
|
|
804
872
|
// Clean Gemini CLI UI artifacts
|
|
805
|
-
|
|
806
|
-
response = cleanGeminiResponse(response);
|
|
807
|
-
}
|
|
873
|
+
const response = state.agent === 'gemini' ? cleanGeminiResponse(extractedResponse) : extractedResponse;
|
|
808
874
|
state.response = response;
|
|
875
|
+
state.truncated = truncated;
|
|
809
876
|
state.status = 'completed';
|
|
810
877
|
state.elapsedMs = elapsedMs;
|
|
811
878
|
clearActiveRequest(paths, state.agent, state.requestId);
|
|
@@ -871,6 +938,7 @@ function outputBroadcastResults(
|
|
|
871
938
|
status: s.status,
|
|
872
939
|
response: s.response,
|
|
873
940
|
partialResponse: s.partialResponse,
|
|
941
|
+
truncated: s.truncated,
|
|
874
942
|
error: s.error,
|
|
875
943
|
elapsedMs: s.elapsedMs,
|
|
876
944
|
})),
|
|
@@ -889,6 +957,9 @@ function outputBroadcastResults(
|
|
|
889
957
|
// Print responses
|
|
890
958
|
for (const state of agentStates) {
|
|
891
959
|
if (state.status === 'completed' && state.response) {
|
|
960
|
+
if (state.truncated) {
|
|
961
|
+
ui.warn(`Response from ${state.agent} may be truncated`);
|
|
962
|
+
}
|
|
892
963
|
console.log(colors.cyan(`─── Response from ${state.agent} (${state.pane}) ───`));
|
|
893
964
|
console.log(state.response);
|
|
894
965
|
console.log();
|
package/src/config.ts
CHANGED
|
@@ -26,7 +26,8 @@ const DEFAULT_CONFIG: GlobalConfig = {
|
|
|
26
26
|
timeout: 180,
|
|
27
27
|
pollInterval: 1,
|
|
28
28
|
captureLines: 100,
|
|
29
|
-
|
|
29
|
+
maxCaptureLines: 2000, // max lines for final extraction (expandable capture)
|
|
30
|
+
preambleEvery: 3, // inject preamble every N messages
|
|
30
31
|
},
|
|
31
32
|
};
|
|
32
33
|
|
package/src/context.test.ts
CHANGED
|
@@ -18,7 +18,7 @@ describe('createContext', () => {
|
|
|
18
18
|
const config: ResolvedConfig = {
|
|
19
19
|
mode: 'polling',
|
|
20
20
|
preambleMode: 'always',
|
|
21
|
-
defaults: { timeout: 180, pollInterval: 1, captureLines: 100, preambleEvery: 3 },
|
|
21
|
+
defaults: { timeout: 180, pollInterval: 1, captureLines: 100, maxCaptureLines: 2000, preambleEvery: 3 },
|
|
22
22
|
agents: {},
|
|
23
23
|
paneRegistry: {},
|
|
24
24
|
};
|
package/src/types.ts
CHANGED
|
@@ -18,6 +18,7 @@ export interface ConfigDefaults {
|
|
|
18
18
|
timeout: number; // seconds
|
|
19
19
|
pollInterval: number; // seconds
|
|
20
20
|
captureLines: number;
|
|
21
|
+
maxCaptureLines: number; // max lines for final extraction (default: 2000)
|
|
21
22
|
preambleEvery: number; // inject preamble every N messages (default: 3)
|
|
22
23
|
}
|
|
23
24
|
|