pi-openai-toolkit 0.20.7 → 0.21.0
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/LICENSE +21 -21
- package/NOTICE +83 -83
- package/README.md +8 -26
- package/README.zh.md +8 -26
- package/config.schema.json +278 -366
- package/docs/configuration.md +294 -310
- package/docs/internals.md +166 -175
- package/package.json +3 -13
- package/src/compact-client.ts +412 -389
- package/src/compaction-errors.ts +96 -0
- package/src/compaction-projection.ts +35 -35
- package/src/config/legacy.ts +519 -717
- package/src/config/migration.ts +103 -100
- package/src/config/notifications.ts +36 -28
- package/src/config/policy.ts +10 -18
- package/src/config/v2.ts +1167 -1380
- package/src/config.ts +310 -319
- package/src/context-management/codex-provider.ts +270 -270
- package/src/context-management/history-notes.ts +357 -357
- package/src/context-management/manual-compact.ts +0 -11
- package/src/context-management/tools.ts +371 -371
- package/src/context-management/types.ts +168 -168
- package/src/context-management/window-manager.ts +801 -801
- package/src/debug.ts +238 -237
- package/src/deferred-tool-carryover.ts +282 -282
- package/src/details-store.ts +151 -151
- package/src/extension-runtime.ts +1662 -1623
- package/src/image-generation/extension.ts +189 -189
- package/src/image-generation/protocol.ts +579 -579
- package/src/image-generation/types.ts +300 -300
- package/src/native-fallback.ts +4 -132
- package/src/payload-rewrite.ts +515 -518
- package/src/pi-context-hook.ts +117 -117
- package/src/remote-v2-client.ts +811 -814
- package/src/request-context-cache.ts +155 -155
- package/src/responses-headers.ts +180 -180
- package/src/runtime.ts +865 -832
- package/src/serializer.ts +882 -882
- package/src/supported-environment.ts +27 -27
- package/src/types.ts +565 -648
- package/src/web-search/alpha-client.ts +876 -876
- package/src/web-search/extension.ts +530 -530
- package/src/web-search/payload.ts +299 -299
- package/src/web-search/prompt.ts +60 -60
- package/src/web-search/types.ts +166 -166
- package/tsconfig.check.json +50 -54
- package/src/auto-mode/classifier.ts +0 -161
- package/src/auto-mode/extension.ts +0 -691
- package/src/auto-mode/model-selection-guard.ts +0 -52
- package/src/auto-mode/policy.ts +0 -146
- package/src/auto-mode/prompt.ts +0 -279
- package/src/auto-mode/reviewer.ts +0 -325
- package/src/auto-mode/tool-review-tui.ts +0 -211
- package/src/auto-mode/transcript.ts +0 -135
- package/src/auto-mode/types.ts +0 -137
package/src/extension-runtime.ts
CHANGED
|
@@ -1,1623 +1,1662 @@
|
|
|
1
|
-
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
2
|
-
import type {
|
|
3
|
-
BeforeProviderRequestEvent,
|
|
4
|
-
ContextWithSystemEvent,
|
|
5
|
-
CompactionResult,
|
|
6
|
-
ExtensionAPI,
|
|
7
|
-
ExtensionContext,
|
|
8
|
-
SessionBeforeCompactEvent,
|
|
9
|
-
} from "@earendil-works/pi-coding-agent";
|
|
10
|
-
import { assertConfigValid, loadToolkitConfig, resolveToolkitConfig, type ResolvedToolkitConfig } from "./config";
|
|
11
|
-
import { notifyConfigIssues } from "./config/notifications";
|
|
12
|
-
import { registerToolkitConfigCommand } from "./config-command";
|
|
13
|
-
import {
|
|
14
|
-
codexContextProviderHeaders,
|
|
15
|
-
resolveCodexContextProvider,
|
|
16
|
-
isCodexGatewayModel,
|
|
17
|
-
isNativeCodexModel,
|
|
18
|
-
} from "./context-management/codex-provider";
|
|
19
|
-
import { routeContextNamespaceToolMessage } from "./context-management/namespace-tools";
|
|
20
|
-
import { loadHistoryNotesThreadHint } from "./context-management/history-notes";
|
|
21
|
-
import { CodexContextWindowManager } from "./context-management/window-manager";
|
|
22
|
-
import { ManagedManualCompact } from "./context-management/manual-compact";
|
|
23
|
-
import {
|
|
24
|
-
registerContextManagementTools,
|
|
25
|
-
type ContextToolRegistrationState,
|
|
26
|
-
} from "./context-management/tools";
|
|
27
|
-
import { writeDebugArtifact, writeReplayFailureArtifact } from "./debug";
|
|
28
|
-
import {
|
|
29
|
-
COMPACTION_CHECKPOINT_PROVENANCE_UNAVAILABLE,
|
|
30
|
-
COMPACTION_CHECKPOINT_CONTEXT_EDITED,
|
|
31
|
-
COMPACTION_PROJECTION_UNAVAILABLE,
|
|
32
|
-
COMPACTION_SESSION_CONTEXT_UNAVAILABLE,
|
|
33
|
-
COMPACTION_PROJECTED_CONTEXT_UNAVAILABLE,
|
|
34
|
-
hasVerifiedCompactionInputProvenance,
|
|
35
|
-
type UnprojectedCompactionReason,
|
|
36
|
-
} from "./compaction-projection";
|
|
37
|
-
import { CODEX_GATEWAY_FORWARD_HEADERS } from "./responses-headers";
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
import {
|
|
55
|
-
import {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
type
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
ctx
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
config
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
case "
|
|
226
|
-
return "context-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
);
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
if (
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
if (
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
);
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
);
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
event
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
event
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
if (
|
|
840
|
-
const
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
},
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
config
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
ctx
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
)
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
)
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
const
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
ctx
|
|
1244
|
-
|
|
1245
|
-
return
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
if (
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
}
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
)
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
)
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
const
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1
|
+
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import type {
|
|
3
|
+
BeforeProviderRequestEvent,
|
|
4
|
+
ContextWithSystemEvent,
|
|
5
|
+
CompactionResult,
|
|
6
|
+
ExtensionAPI,
|
|
7
|
+
ExtensionContext,
|
|
8
|
+
SessionBeforeCompactEvent,
|
|
9
|
+
} from "@earendil-works/pi-coding-agent";
|
|
10
|
+
import { assertConfigValid, loadToolkitConfig, resolveToolkitConfig, type ResolvedToolkitConfig } from "./config";
|
|
11
|
+
import { notifyConfigIssues } from "./config/notifications";
|
|
12
|
+
import { registerToolkitConfigCommand } from "./config-command";
|
|
13
|
+
import {
|
|
14
|
+
codexContextProviderHeaders,
|
|
15
|
+
resolveCodexContextProvider,
|
|
16
|
+
isCodexGatewayModel,
|
|
17
|
+
isNativeCodexModel,
|
|
18
|
+
} from "./context-management/codex-provider";
|
|
19
|
+
import { routeContextNamespaceToolMessage } from "./context-management/namespace-tools";
|
|
20
|
+
import { loadHistoryNotesThreadHint } from "./context-management/history-notes";
|
|
21
|
+
import { CodexContextWindowManager } from "./context-management/window-manager";
|
|
22
|
+
import { ManagedManualCompact } from "./context-management/manual-compact";
|
|
23
|
+
import {
|
|
24
|
+
registerContextManagementTools,
|
|
25
|
+
type ContextToolRegistrationState,
|
|
26
|
+
} from "./context-management/tools";
|
|
27
|
+
import { writeDebugArtifact, writeReplayFailureArtifact } from "./debug";
|
|
28
|
+
import {
|
|
29
|
+
COMPACTION_CHECKPOINT_PROVENANCE_UNAVAILABLE,
|
|
30
|
+
COMPACTION_CHECKPOINT_CONTEXT_EDITED,
|
|
31
|
+
COMPACTION_PROJECTION_UNAVAILABLE,
|
|
32
|
+
COMPACTION_SESSION_CONTEXT_UNAVAILABLE,
|
|
33
|
+
COMPACTION_PROJECTED_CONTEXT_UNAVAILABLE,
|
|
34
|
+
hasVerifiedCompactionInputProvenance,
|
|
35
|
+
type UnprojectedCompactionReason,
|
|
36
|
+
} from "./compaction-projection";
|
|
37
|
+
import { CODEX_GATEWAY_FORWARD_HEADERS } from "./responses-headers";
|
|
38
|
+
import { executeNativeCompaction, type NativeCompactionClientResult } from "./compact-client";
|
|
39
|
+
import { qualifiesForStandaloneCompaction, type CompactionErrorInfo } from "./compaction-errors";
|
|
40
|
+
import { resolveLatestNativeCompactionEntry } from "./details-store";
|
|
41
|
+
import {
|
|
42
|
+
getPiContextHookProjector,
|
|
43
|
+
installPiContextHookPatch,
|
|
44
|
+
reportPiContextHookFailure,
|
|
45
|
+
type PiContextHookPatchResult,
|
|
46
|
+
} from "./pi-context-hook";
|
|
47
|
+
import { runNativeFallbackCompaction } from "./native-fallback";
|
|
48
|
+
import {
|
|
49
|
+
rewriteResponsesPayloadWithNativeReplay,
|
|
50
|
+
hasInvalidatedCompactionContext,
|
|
51
|
+
removeNativeCompactionRetainedMessages,
|
|
52
|
+
serializeLiveTailToResponsesInputWithLimits,
|
|
53
|
+
} from "./payload-rewrite";
|
|
54
|
+
import { getCompactionRequestExtras, rememberRequestContext } from "./request-context-cache";
|
|
55
|
+
import { resolveWebSearchRoute } from "./web-search/types";
|
|
56
|
+
import { executeRemoteV2Compaction } from "./remote-v2-client";
|
|
57
|
+
import {
|
|
58
|
+
rememberPhysicalModel,
|
|
59
|
+
resolveEffectiveModel,
|
|
60
|
+
resolveNativeCompactionEnvironment,
|
|
61
|
+
resolveRemoteCompactionExecution,
|
|
62
|
+
isStandaloneCompactionEligible,
|
|
63
|
+
parseModelSpec,
|
|
64
|
+
type RemoteCompactionExecution,
|
|
65
|
+
} from "./runtime";
|
|
66
|
+
import {
|
|
67
|
+
serializeMessagesToCompactRequestWithLimits,
|
|
68
|
+
serializeMessagesToResponsesInputWithLimits,
|
|
69
|
+
type NativeCompactionRequestBody,
|
|
70
|
+
type ResponsesInputItem,
|
|
71
|
+
} from "./serializer";
|
|
72
|
+
import {
|
|
73
|
+
createNativeCompactionDetails,
|
|
74
|
+
createNativeCompactionResult,
|
|
75
|
+
COMPACTION_EXTENSION_ID,
|
|
76
|
+
LEGACY_NATIVE_COMPACTION_STRATEGY,
|
|
77
|
+
REMOTE_V2_COMPACTION_STRATEGY,
|
|
78
|
+
getLatestDeferredToolCarryover,
|
|
79
|
+
getRemoteV2InputProvenance,
|
|
80
|
+
isNativeCompactionDetails,
|
|
81
|
+
type CompactionConfig,
|
|
82
|
+
type NativeCompactionDetails,
|
|
83
|
+
type NativeCompactionRequestMeta,
|
|
84
|
+
} from "./types";
|
|
85
|
+
|
|
86
|
+
type CompactionContextProjection = (
|
|
87
|
+
messages: readonly AgentMessage[],
|
|
88
|
+
ctx: ExtensionContext,
|
|
89
|
+
) => readonly AgentMessage[] | undefined | Promise<readonly AgentMessage[] | undefined>;
|
|
90
|
+
|
|
91
|
+
function contextOperation(loadConfig: typeof loadToolkitConfig, ctx: ExtensionContext, model = ctx.model): ResolvedToolkitConfig {
|
|
92
|
+
const loaded = loadConfig();
|
|
93
|
+
const resolved = resolveToolkitConfig(loaded, model);
|
|
94
|
+
notifyConfigIssues(ctx, resolved);
|
|
95
|
+
return resolved;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function requireContextPolicy(resolved: ResolvedToolkitConfig, ctx: ExtensionContext): void {
|
|
99
|
+
try {
|
|
100
|
+
assertConfigValid(resolved, "context", "compatibility", "diagnostics");
|
|
101
|
+
} catch (error) {
|
|
102
|
+
if (typeof ctx.abort === "function") ctx.abort();
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function compactGatewayModels(resolved: ResolvedToolkitConfig): readonly string[] {
|
|
108
|
+
return resolved.format === "v2" || resolved.config.compaction.contextManagement === "remote"
|
|
109
|
+
? resolved.gatewayModelKeys : [];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
type CompactionDependencies = {
|
|
113
|
+
loadConfig: typeof loadToolkitConfig;
|
|
114
|
+
remoteCompact: typeof executeRemoteV2Compaction;
|
|
115
|
+
standaloneCompact: typeof executeNativeCompaction;
|
|
116
|
+
nativeFallback: typeof runNativeFallbackCompaction;
|
|
117
|
+
contextWindows: CodexContextWindowManager;
|
|
118
|
+
manualCompact?: ManagedManualCompact;
|
|
119
|
+
/** Test seam for the same ordered projection exposed by the Pi host patch. */
|
|
120
|
+
projectCompactionContext?: CompactionContextProjection;
|
|
121
|
+
piContextHookPatch: PiContextHookPatchResult;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
type RemoteContextActive = (
|
|
125
|
+
ctx: ExtensionContext,
|
|
126
|
+
config: CompactionConfig,
|
|
127
|
+
model?: ExtensionContext["model"],
|
|
128
|
+
) => Promise<boolean>;
|
|
129
|
+
type ContextToolRegistrationStateReader = () => ContextToolRegistrationState;
|
|
130
|
+
|
|
131
|
+
type ContextToolSyncOutcome = {
|
|
132
|
+
eligible: boolean;
|
|
133
|
+
toolsSynced: boolean;
|
|
134
|
+
registrationState: ContextToolRegistrationState;
|
|
135
|
+
windowInitialized: boolean;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
type ResponsesCompactOutcome =
|
|
139
|
+
| { outcome: "success"; compaction: CompactionResult<NativeCompactionDetails> }
|
|
140
|
+
| { outcome: "aborted" }
|
|
141
|
+
| { outcome: "failed"; remoteFailure?: CompactionErrorInfo & { reason: string; status?: number }; protocol?: string }
|
|
142
|
+
| {
|
|
143
|
+
outcome: "unprojected-input";
|
|
144
|
+
reason: UnprojectedCompactionReason;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
function buildCompactionRequestMeta(event: SessionBeforeCompactEvent): NativeCompactionRequestMeta {
|
|
148
|
+
return {
|
|
149
|
+
tokensBefore: event.preparation.tokensBefore,
|
|
150
|
+
previousSummaryPresent: Boolean(event.preparation.previousSummary),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function getCurrentModelDebugInfo(ctx: ExtensionContext) {
|
|
155
|
+
return ctx.model
|
|
156
|
+
? {
|
|
157
|
+
provider: ctx.model.provider,
|
|
158
|
+
id: ctx.model.id,
|
|
159
|
+
}
|
|
160
|
+
: undefined;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function getCompactionIdentityDebugInfo(entry: { details?: unknown } | undefined) {
|
|
164
|
+
return isNativeCompactionDetails(entry?.details)
|
|
165
|
+
? {
|
|
166
|
+
provider: entry.details.provider,
|
|
167
|
+
api: entry.details.api,
|
|
168
|
+
model: entry.details.model,
|
|
169
|
+
baseUrl: entry.details.baseUrl,
|
|
170
|
+
compactionModel: entry.details.compactionModel,
|
|
171
|
+
}
|
|
172
|
+
: undefined;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function getSessionId(ctx: ExtensionContext): string | undefined {
|
|
176
|
+
try {
|
|
177
|
+
return ctx.sessionManager.getSessionId();
|
|
178
|
+
} catch {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function notifyWarning(ctx: ExtensionContext, message: string): void {
|
|
184
|
+
if (ctx.hasUI) {
|
|
185
|
+
ctx.ui.notify(`${COMPACTION_EXTENSION_ID}: ${message}`, "warning");
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function resolveContextCapabilityModel(
|
|
190
|
+
ctx: ExtensionContext,
|
|
191
|
+
model: ExtensionContext["model"] = ctx.model,
|
|
192
|
+
): Promise<ExtensionContext["model"]> {
|
|
193
|
+
const effective = await resolveEffectiveModel(ctx, model);
|
|
194
|
+
return effective.ok ? effective.model : undefined;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async function isRemoteContextActive(
|
|
198
|
+
ctx: ExtensionContext,
|
|
199
|
+
config: CompactionConfig,
|
|
200
|
+
model: ExtensionContext["model"] = ctx.model,
|
|
201
|
+
): Promise<boolean> {
|
|
202
|
+
if (!config.enabled || config.contextManagement !== "remote") return false;
|
|
203
|
+
const resolution = await resolveCodexContextProvider(ctx, model, config.gatewayContextModels);
|
|
204
|
+
return resolution.ok;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function isCodexContextModel(
|
|
208
|
+
model: ExtensionContext["model"] | undefined,
|
|
209
|
+
config: CompactionConfig,
|
|
210
|
+
): boolean {
|
|
211
|
+
return config.contextManagement === "remote"
|
|
212
|
+
&& (isNativeCodexModel(model) || isCodexGatewayModel(model, config.gatewayContextModels));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function isExcludedGatewayContext(
|
|
216
|
+
model: ExtensionContext["model"] | undefined,
|
|
217
|
+
config: CompactionConfig,
|
|
218
|
+
registrationState: ContextToolRegistrationState,
|
|
219
|
+
): boolean {
|
|
220
|
+
return registrationState === "excluded" && isCodexGatewayModel(model, config.gatewayContextModels);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function contextManagementFailureReason(registrationState: ContextToolRegistrationState): string {
|
|
224
|
+
switch (registrationState) {
|
|
225
|
+
case "excluded":
|
|
226
|
+
return "context-tools-excluded";
|
|
227
|
+
case "conflict":
|
|
228
|
+
return "tool-name-conflict";
|
|
229
|
+
case "unverified":
|
|
230
|
+
return "tool-registration-pending";
|
|
231
|
+
case "verified":
|
|
232
|
+
return "context-management-unavailable";
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function notifyRemoteContextFailure(ctx: ExtensionContext, reason: string): void {
|
|
237
|
+
if (ctx.hasUI) {
|
|
238
|
+
ctx.ui.notify(`${COMPACTION_EXTENSION_ID}: Remote Context management inactive (${reason})`, "warning");
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Surface every window trim that had to be refused to keep the tool loadout intact.
|
|
244
|
+
*
|
|
245
|
+
* The refusal itself is safe: the model keeps the previous turns instead of losing its
|
|
246
|
+
* tools. It is still a continuity incident, so it is recorded once per window and reason
|
|
247
|
+
* instead of being allowed to pass silently.
|
|
248
|
+
*/
|
|
249
|
+
function reportProjectionDiagnostics(
|
|
250
|
+
contextWindows: CodexContextWindowManager,
|
|
251
|
+
config: CompactionConfig,
|
|
252
|
+
ctx: ExtensionContext,
|
|
253
|
+
): void {
|
|
254
|
+
const diagnostics = contextWindows.takeProjectionDiagnostics();
|
|
255
|
+
if (diagnostics.length === 0) return;
|
|
256
|
+
writeDebugArtifact(
|
|
257
|
+
"compaction-event",
|
|
258
|
+
{ event: "context.projection.tool_loadout_shrink", diagnostics },
|
|
259
|
+
config,
|
|
260
|
+
ctx,
|
|
261
|
+
);
|
|
262
|
+
if (ctx.hasUI) {
|
|
263
|
+
ctx.ui.notify(
|
|
264
|
+
`${COMPACTION_EXTENSION_ID}: kept previous context to preserve tools (${diagnostics.join("; ")})`,
|
|
265
|
+
"warning",
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function cloneOpaqueWindow(window: readonly unknown[]): unknown[] {
|
|
271
|
+
return window.map((item) => structuredClone(item));
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function buildCompactionInstructions(systemPrompt: string, customInstructions?: string): string {
|
|
275
|
+
const guidance = customInstructions?.trim();
|
|
276
|
+
if (!guidance) {
|
|
277
|
+
return systemPrompt;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return `${systemPrompt}\n\nAdditional user guidance for this manual /compact request:\n${guidance}`;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function findLatestProjectedCompactionSummaryIndex(
|
|
284
|
+
messages: readonly AgentMessage[],
|
|
285
|
+
summary: string,
|
|
286
|
+
): number {
|
|
287
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
288
|
+
const message = messages[index];
|
|
289
|
+
if (message?.role === "compactionSummary" && message.summary === summary) {
|
|
290
|
+
return index;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return -1;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function readSessionContextMessages(ctx: ExtensionContext): AgentMessage[] | undefined {
|
|
298
|
+
try {
|
|
299
|
+
const projection = ctx.sessionManager.buildSessionProjection();
|
|
300
|
+
if (!projection || !Array.isArray(projection.messages)) return undefined;
|
|
301
|
+
return structuredClone(projection.messages);
|
|
302
|
+
} catch {
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
type CompactionProjectionResult =
|
|
308
|
+
| { ok: true; messages: readonly AgentMessage[] }
|
|
309
|
+
| { ok: false; reason: typeof COMPACTION_PROJECTION_UNAVAILABLE | typeof COMPACTION_SESSION_CONTEXT_UNAVAILABLE };
|
|
310
|
+
|
|
311
|
+
async function projectSessionContextForCompaction(
|
|
312
|
+
ctx: ExtensionContext,
|
|
313
|
+
projectCompactionContext: CompactionContextProjection | undefined,
|
|
314
|
+
): Promise<CompactionProjectionResult> {
|
|
315
|
+
const messages = readSessionContextMessages(ctx);
|
|
316
|
+
if (!messages) {
|
|
317
|
+
return { ok: false, reason: COMPACTION_SESSION_CONTEXT_UNAVAILABLE };
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const projector = projectCompactionContext ?? getPiContextHookProjector(ctx);
|
|
321
|
+
if (!projector) {
|
|
322
|
+
return { ok: false, reason: COMPACTION_PROJECTION_UNAVAILABLE };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
const projected = await projector(messages, ctx);
|
|
327
|
+
return Array.isArray(projected)
|
|
328
|
+
? { ok: true, messages: projected }
|
|
329
|
+
: { ok: false, reason: COMPACTION_PROJECTION_UNAVAILABLE };
|
|
330
|
+
} catch {
|
|
331
|
+
return { ok: false, reason: COMPACTION_PROJECTION_UNAVAILABLE };
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function getLegacySessionContextMessages(
|
|
336
|
+
event: SessionBeforeCompactEvent,
|
|
337
|
+
ctx: ExtensionContext,
|
|
338
|
+
): AgentMessage[] {
|
|
339
|
+
return readSessionContextMessages(ctx) ?? [
|
|
340
|
+
...event.preparation.messagesToSummarize,
|
|
341
|
+
...event.preparation.turnPrefixMessages,
|
|
342
|
+
];
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async function runResponsesNativeCompact(
|
|
346
|
+
event: SessionBeforeCompactEvent,
|
|
347
|
+
ctx: ExtensionContext,
|
|
348
|
+
config: CompactionConfig,
|
|
349
|
+
execution: RemoteCompactionExecution,
|
|
350
|
+
remoteCompact: typeof executeRemoteV2Compaction,
|
|
351
|
+
projectCompactionContext: CompactionContextProjection | undefined,
|
|
352
|
+
/** Only ordinary compaction supplies this; managed recovery remains V2-only. */
|
|
353
|
+
standaloneCompact?: typeof executeNativeCompaction,
|
|
354
|
+
): Promise<ResponsesCompactOutcome> {
|
|
355
|
+
const { consumer, compactor } = execution;
|
|
356
|
+
const instructions = buildCompactionInstructions(ctx.getSystemPrompt(), event.customInstructions);
|
|
357
|
+
const branchEntries = event.branchEntries ?? ctx.sessionManager.getBranch();
|
|
358
|
+
const deferredToolCarryover = getLatestDeferredToolCarryover(branchEntries);
|
|
359
|
+
const latestNativeCompaction = resolveLatestNativeCompactionEntry(branchEntries, {
|
|
360
|
+
baseUrl: consumer.baseUrl,
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
const inputProvenance = getRemoteV2InputProvenance(config.remoteV2ContextSource);
|
|
364
|
+
let requestSource: "session-context" | "non-native-session-context" | "latest-native-replay";
|
|
365
|
+
let request: NativeCompactionRequestBody;
|
|
366
|
+
if (latestNativeCompaction.ok) {
|
|
367
|
+
if (hasInvalidatedCompactionContext(branchEntries, latestNativeCompaction.entry.id)) {
|
|
368
|
+
return { outcome: "unprojected-input", reason: COMPACTION_CHECKPOINT_CONTEXT_EDITED };
|
|
369
|
+
}
|
|
370
|
+
const details = latestNativeCompaction.entry.details;
|
|
371
|
+
if (!details) {
|
|
372
|
+
return { outcome: "failed" };
|
|
373
|
+
}
|
|
374
|
+
if (!hasVerifiedCompactionInputProvenance(details, inputProvenance)) {
|
|
375
|
+
return {
|
|
376
|
+
outcome: "unprojected-input",
|
|
377
|
+
reason: COMPACTION_CHECKPOINT_PROVENANCE_UNAVAILABLE,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
requestSource = "latest-native-replay";
|
|
381
|
+
|
|
382
|
+
if (config.remoteV2ContextSource === "pi-context-hook") {
|
|
383
|
+
const projection = await projectSessionContextForCompaction(ctx, projectCompactionContext);
|
|
384
|
+
if (!projection.ok) {
|
|
385
|
+
return { outcome: "unprojected-input", reason: projection.reason };
|
|
386
|
+
}
|
|
387
|
+
const summaryIndex = findLatestProjectedCompactionSummaryIndex(
|
|
388
|
+
projection.messages,
|
|
389
|
+
latestNativeCompaction.entry.summary,
|
|
390
|
+
);
|
|
391
|
+
if (summaryIndex < 0) {
|
|
392
|
+
return {
|
|
393
|
+
outcome: "unprojected-input",
|
|
394
|
+
reason: COMPACTION_PROJECTED_CONTEXT_UNAVAILABLE,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
const input: ResponsesInputItem[] = [
|
|
398
|
+
...(cloneOpaqueWindow(details.compactedWindow) as ResponsesInputItem[]),
|
|
399
|
+
...(await serializeMessagesToResponsesInputWithLimits(
|
|
400
|
+
compactor.currentModel,
|
|
401
|
+
[...projection.messages.slice(summaryIndex + 1)],
|
|
402
|
+
{ firstSystemMessageIsUpdate: true },
|
|
403
|
+
)),
|
|
404
|
+
];
|
|
405
|
+
request = {
|
|
406
|
+
model: compactor.requestModel ?? compactor.model,
|
|
407
|
+
input,
|
|
408
|
+
instructions,
|
|
409
|
+
};
|
|
410
|
+
} else {
|
|
411
|
+
const liveTailEntries = branchEntries.slice(latestNativeCompaction.index + 1);
|
|
412
|
+
const input: ResponsesInputItem[] = [
|
|
413
|
+
...(cloneOpaqueWindow(details.compactedWindow) as ResponsesInputItem[]),
|
|
414
|
+
...(await serializeLiveTailToResponsesInputWithLimits({ model: compactor.currentModel, entries: liveTailEntries })),
|
|
415
|
+
];
|
|
416
|
+
request = {
|
|
417
|
+
model: compactor.requestModel ?? compactor.model,
|
|
418
|
+
input,
|
|
419
|
+
instructions,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
} else if (
|
|
423
|
+
latestNativeCompaction.reason === "no-compaction" ||
|
|
424
|
+
(latestNativeCompaction.reason === "latest-compaction-not-native" &&
|
|
425
|
+
config.allowCompactionContinuityBreak)
|
|
426
|
+
) {
|
|
427
|
+
requestSource =
|
|
428
|
+
latestNativeCompaction.reason === "no-compaction" ? "session-context" : "non-native-session-context";
|
|
429
|
+
if (config.remoteV2ContextSource === "pi-context-hook") {
|
|
430
|
+
const projection = await projectSessionContextForCompaction(ctx, projectCompactionContext);
|
|
431
|
+
if (!projection.ok) {
|
|
432
|
+
return { outcome: "unprojected-input", reason: projection.reason };
|
|
433
|
+
}
|
|
434
|
+
const serialized = await serializeMessagesToCompactRequestWithLimits({
|
|
435
|
+
model: compactor.currentModel,
|
|
436
|
+
messages: [...projection.messages],
|
|
437
|
+
instructions,
|
|
438
|
+
});
|
|
439
|
+
request = { ...serialized, model: compactor.requestModel ?? serialized.model };
|
|
440
|
+
} else {
|
|
441
|
+
const serialized = await serializeMessagesToCompactRequestWithLimits({
|
|
442
|
+
model: compactor.currentModel,
|
|
443
|
+
messages: getLegacySessionContextMessages(event, ctx),
|
|
444
|
+
instructions,
|
|
445
|
+
});
|
|
446
|
+
request = { ...serialized, model: compactor.requestModel ?? serialized.model };
|
|
447
|
+
}
|
|
448
|
+
} else {
|
|
449
|
+
writeDebugArtifact(
|
|
450
|
+
"compaction-event",
|
|
451
|
+
{
|
|
452
|
+
event: "session_before_compact.remote-v2-skip",
|
|
453
|
+
reason: latestNativeCompaction.reason,
|
|
454
|
+
consumer: {
|
|
455
|
+
provider: consumer.provider,
|
|
456
|
+
api: consumer.api,
|
|
457
|
+
model: consumer.model,
|
|
458
|
+
baseUrl: consumer.baseUrl,
|
|
459
|
+
},
|
|
460
|
+
compactor: {
|
|
461
|
+
provider: compactor.provider,
|
|
462
|
+
api: compactor.api,
|
|
463
|
+
model: compactor.model,
|
|
464
|
+
baseUrl: compactor.baseUrl,
|
|
465
|
+
},
|
|
466
|
+
latestCompactionIndex: latestNativeCompaction.latestCompactionIndex,
|
|
467
|
+
latestCompactionIdentity: getCompactionIdentityDebugInfo(latestNativeCompaction.latestCompaction),
|
|
468
|
+
},
|
|
469
|
+
config,
|
|
470
|
+
ctx,
|
|
471
|
+
);
|
|
472
|
+
return { outcome: "failed" };
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Mirror the latest codex_rs CompactionInput fields captured from the most
|
|
476
|
+
// recent live provider request for this model (tools, reasoning, etc.).
|
|
477
|
+
const extras = getCompactionRequestExtras({
|
|
478
|
+
provider: consumer.provider,
|
|
479
|
+
api: consumer.api,
|
|
480
|
+
model: consumer.requestModel ?? consumer.model,
|
|
481
|
+
baseUrl: consumer.baseUrl,
|
|
482
|
+
sessionId: getSessionId(ctx),
|
|
483
|
+
}, compactor.currentModel);
|
|
484
|
+
if (extras) {
|
|
485
|
+
request = { ...request, ...extras };
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (event.signal?.aborted) return { outcome: "aborted" };
|
|
489
|
+
let protocol: "remote-v2" | "standalone-compact" = "remote-v2";
|
|
490
|
+
let compactResult: Awaited<ReturnType<typeof executeRemoteV2Compaction>> | NativeCompactionClientResult = await remoteCompact({
|
|
491
|
+
runtime: compactor,
|
|
492
|
+
request,
|
|
493
|
+
signal: event.signal,
|
|
494
|
+
settings: config,
|
|
495
|
+
context: ctx,
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
if (!compactResult.ok) {
|
|
499
|
+
if (compactResult.reason === "aborted" || event.signal?.aborted) return { outcome: "aborted" };
|
|
500
|
+
const tryStandalone = !!standaloneCompact && isStandaloneCompactionEligible(compactor) && qualifiesForStandaloneCompaction(compactResult);
|
|
501
|
+
writeDebugArtifact("compaction-event", {
|
|
502
|
+
event: "session_before_compact.remote-v2-failure",
|
|
503
|
+
protocol,
|
|
504
|
+
reason: compactResult.reason,
|
|
505
|
+
status: compactResult.status,
|
|
506
|
+
errorCode: compactResult.errorCode,
|
|
507
|
+
errorType: compactResult.errorType,
|
|
508
|
+
errorParam: compactResult.errorParam,
|
|
509
|
+
errorMessage: compactResult.errorMessage,
|
|
510
|
+
nextStep: tryStandalone ? "standalone-compact" : "native-summary",
|
|
511
|
+
}, config, ctx);
|
|
512
|
+
if (tryStandalone && standaloneCompact) {
|
|
513
|
+
if (event.signal?.aborted) return { outcome: "aborted" };
|
|
514
|
+
protocol = "standalone-compact";
|
|
515
|
+
compactResult = await standaloneCompact({ runtime: compactor, request, signal: event.signal, settings: config, context: ctx });
|
|
516
|
+
}
|
|
517
|
+
if (!compactResult.ok) {
|
|
518
|
+
if (compactResult.reason === "aborted" || event.signal?.aborted) return { outcome: "aborted" };
|
|
519
|
+
const remoteFailure = {
|
|
520
|
+
reason: compactResult.reason,
|
|
521
|
+
status: compactResult.status,
|
|
522
|
+
errorCode: compactResult.errorCode,
|
|
523
|
+
errorType: compactResult.errorType,
|
|
524
|
+
errorParam: compactResult.errorParam,
|
|
525
|
+
errorMessage: compactResult.errorMessage,
|
|
526
|
+
};
|
|
527
|
+
if (protocol === "standalone-compact") writeDebugArtifact("compaction-event", {
|
|
528
|
+
event: "session_before_compact.standalone-compact-failure", protocol, ...remoteFailure, nextStep: "native-summary",
|
|
529
|
+
}, config, ctx);
|
|
530
|
+
return { outcome: "failed", remoteFailure, protocol };
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
let details: NativeCompactionDetails;
|
|
535
|
+
try {
|
|
536
|
+
details = createNativeCompactionDetails({
|
|
537
|
+
strategy: protocol === "standalone-compact" ? LEGACY_NATIVE_COMPACTION_STRATEGY : REMOTE_V2_COMPACTION_STRATEGY,
|
|
538
|
+
provider: consumer.provider,
|
|
539
|
+
api: consumer.api,
|
|
540
|
+
model: consumer.model,
|
|
541
|
+
baseUrl: consumer.baseUrl,
|
|
542
|
+
compactionModel: {
|
|
543
|
+
provider: compactor.provider,
|
|
544
|
+
api: compactor.api,
|
|
545
|
+
model: compactor.model,
|
|
546
|
+
baseUrl: compactor.baseUrl,
|
|
547
|
+
},
|
|
548
|
+
deferredToolCarryover,
|
|
549
|
+
compactedWindow: compactResult.compactedWindow,
|
|
550
|
+
compactResponseId: compactResult.compactResponseId,
|
|
551
|
+
createdAt: compactResult.createdAt,
|
|
552
|
+
requestMeta: buildCompactionRequestMeta(event),
|
|
553
|
+
inputProvenance,
|
|
554
|
+
});
|
|
555
|
+
} catch (error) {
|
|
556
|
+
writeDebugArtifact(
|
|
557
|
+
"compaction-event",
|
|
558
|
+
{
|
|
559
|
+
event: "session_before_compact.invalid-native-details",
|
|
560
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
561
|
+
consumer: {
|
|
562
|
+
provider: consumer.provider,
|
|
563
|
+
api: consumer.api,
|
|
564
|
+
model: consumer.model,
|
|
565
|
+
baseUrl: consumer.baseUrl,
|
|
566
|
+
},
|
|
567
|
+
compactor: {
|
|
568
|
+
provider: compactor.provider,
|
|
569
|
+
api: compactor.api,
|
|
570
|
+
model: compactor.model,
|
|
571
|
+
baseUrl: compactor.baseUrl,
|
|
572
|
+
},
|
|
573
|
+
},
|
|
574
|
+
config,
|
|
575
|
+
ctx,
|
|
576
|
+
);
|
|
577
|
+
return { outcome: "failed", remoteFailure: { reason: "invalid-native-details" }, protocol };
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const compaction = createNativeCompactionResult({
|
|
581
|
+
firstKeptEntryId: event.preparation.firstKeptEntryId,
|
|
582
|
+
tokensBefore: event.preparation.tokensBefore,
|
|
583
|
+
details,
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
writeDebugArtifact(
|
|
587
|
+
"compaction-event",
|
|
588
|
+
{
|
|
589
|
+
event: `session_before_compact.${protocol}-success`,
|
|
590
|
+
protocol,
|
|
591
|
+
consumer: {
|
|
592
|
+
provider: consumer.provider,
|
|
593
|
+
api: consumer.api,
|
|
594
|
+
model: consumer.model,
|
|
595
|
+
baseUrl: consumer.baseUrl,
|
|
596
|
+
},
|
|
597
|
+
compactor: {
|
|
598
|
+
provider: compactor.provider,
|
|
599
|
+
api: compactor.api,
|
|
600
|
+
model: compactor.model,
|
|
601
|
+
baseUrl: compactor.baseUrl,
|
|
602
|
+
},
|
|
603
|
+
requestSource,
|
|
604
|
+
remoteV2ContextSource: config.remoteV2ContextSource,
|
|
605
|
+
inputProvenance,
|
|
606
|
+
requestInputItems: request.input.length,
|
|
607
|
+
requestExtras: extras ? Object.keys(extras) : [],
|
|
608
|
+
compactResponseId: compactResult.compactResponseId,
|
|
609
|
+
compactedItems: compactResult.compactedWindow.length,
|
|
610
|
+
firstKeptEntryId: event.preparation.firstKeptEntryId,
|
|
611
|
+
},
|
|
612
|
+
config,
|
|
613
|
+
ctx,
|
|
614
|
+
);
|
|
615
|
+
|
|
616
|
+
return { outcome: "success", compaction };
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
type ContextUnavailableCompactionResult =
|
|
620
|
+
| { compaction: CompactionResult<unknown> }
|
|
621
|
+
| { cancel: true };
|
|
622
|
+
|
|
623
|
+
async function runUnavailableContextCompaction(
|
|
624
|
+
event: SessionBeforeCompactEvent,
|
|
625
|
+
ctx: ExtensionContext,
|
|
626
|
+
dependencies: CompactionDependencies,
|
|
627
|
+
resolved: ResolvedToolkitConfig,
|
|
628
|
+
config: CompactionConfig,
|
|
629
|
+
registrationState: ContextToolRegistrationState,
|
|
630
|
+
): Promise<ContextUnavailableCompactionResult> {
|
|
631
|
+
const contextReason = contextManagementFailureReason(registrationState);
|
|
632
|
+
const remoteModelSpec = config.remoteCompactModel?.trim();
|
|
633
|
+
let remoteFailure: string | undefined;
|
|
634
|
+
|
|
635
|
+
if (remoteModelSpec) {
|
|
636
|
+
const resolution = await resolveRemoteCompactionExecution(
|
|
637
|
+
ctx,
|
|
638
|
+
{
|
|
639
|
+
enabled: config.enabled,
|
|
640
|
+
responsesApis: config.responsesApis,
|
|
641
|
+
codexGatewayModels: compactGatewayModels(resolved),
|
|
642
|
+
},
|
|
643
|
+
remoteModelSpec,
|
|
644
|
+
);
|
|
645
|
+
if (resolution.ok) {
|
|
646
|
+
const outcome = await runResponsesNativeCompact(
|
|
647
|
+
event,
|
|
648
|
+
ctx,
|
|
649
|
+
config,
|
|
650
|
+
resolution.execution,
|
|
651
|
+
dependencies.remoteCompact,
|
|
652
|
+
dependencies.projectCompactionContext,
|
|
653
|
+
);
|
|
654
|
+
if (outcome.outcome === "success") {
|
|
655
|
+
writeDebugArtifact(
|
|
656
|
+
"compaction-event",
|
|
657
|
+
{
|
|
658
|
+
event: "session_before_compact.context-fallback-success",
|
|
659
|
+
contextReason,
|
|
660
|
+
registrationState,
|
|
661
|
+
compactor: remoteModelSpec,
|
|
662
|
+
},
|
|
663
|
+
config,
|
|
664
|
+
ctx,
|
|
665
|
+
);
|
|
666
|
+
return { compaction: outcome.compaction };
|
|
667
|
+
}
|
|
668
|
+
if (outcome.outcome === "aborted" || outcome.outcome === "unprojected-input") {
|
|
669
|
+
return { cancel: true };
|
|
670
|
+
}
|
|
671
|
+
remoteFailure = "remote-v2-failed";
|
|
672
|
+
} else {
|
|
673
|
+
remoteFailure = resolution.reason;
|
|
674
|
+
writeDebugArtifact(
|
|
675
|
+
"compaction-event",
|
|
676
|
+
{
|
|
677
|
+
event: "session_before_compact.context-fallback-unavailable",
|
|
678
|
+
contextReason,
|
|
679
|
+
registrationState,
|
|
680
|
+
reason: resolution.reason,
|
|
681
|
+
modelSpec: remoteModelSpec,
|
|
682
|
+
errorMessage: resolution.errorMessage,
|
|
683
|
+
},
|
|
684
|
+
config,
|
|
685
|
+
ctx,
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
} else {
|
|
689
|
+
remoteFailure = "missing-remote-compaction-model";
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
const nativeModelSpec = config.nativeFallback.model?.trim();
|
|
693
|
+
if (config.nativeFallback.enabled && nativeModelSpec) {
|
|
694
|
+
const fallback = await dependencies.nativeFallback({
|
|
695
|
+
ctx,
|
|
696
|
+
event,
|
|
697
|
+
config,
|
|
698
|
+
modelSpec: nativeModelSpec,
|
|
699
|
+
});
|
|
700
|
+
if (fallback.ok) {
|
|
701
|
+
writeDebugArtifact(
|
|
702
|
+
"compaction-event",
|
|
703
|
+
{
|
|
704
|
+
event: "session_before_compact.context-fallback-native-success",
|
|
705
|
+
contextReason,
|
|
706
|
+
registrationState,
|
|
707
|
+
remoteFailure,
|
|
708
|
+
fallbackModel: nativeModelSpec,
|
|
709
|
+
},
|
|
710
|
+
config,
|
|
711
|
+
ctx,
|
|
712
|
+
);
|
|
713
|
+
return { compaction: fallback.result };
|
|
714
|
+
}
|
|
715
|
+
writeDebugArtifact(
|
|
716
|
+
"compaction-event",
|
|
717
|
+
{
|
|
718
|
+
event: "session_before_compact.context-fallback-native-failure",
|
|
719
|
+
contextReason,
|
|
720
|
+
registrationState,
|
|
721
|
+
remoteFailure,
|
|
722
|
+
fallbackReason: fallback.reason,
|
|
723
|
+
fallbackModel: nativeModelSpec,
|
|
724
|
+
errorMessage: fallback.errorMessage,
|
|
725
|
+
},
|
|
726
|
+
config,
|
|
727
|
+
ctx,
|
|
728
|
+
);
|
|
729
|
+
} else {
|
|
730
|
+
writeDebugArtifact(
|
|
731
|
+
"compaction-event",
|
|
732
|
+
{
|
|
733
|
+
event: "session_before_compact.context-fallback-cancelled",
|
|
734
|
+
contextReason,
|
|
735
|
+
registrationState,
|
|
736
|
+
remoteFailure,
|
|
737
|
+
reason: config.nativeFallback.enabled ? "missing-native-fallback-model" : "native-fallback-disabled",
|
|
738
|
+
},
|
|
739
|
+
config,
|
|
740
|
+
ctx,
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
return { cancel: true };
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
async function handleSessionBeforeCompact(
|
|
748
|
+
event: SessionBeforeCompactEvent,
|
|
749
|
+
ctx: ExtensionContext,
|
|
750
|
+
dependencies: CompactionDependencies,
|
|
751
|
+
remoteContextActive: RemoteContextActive,
|
|
752
|
+
getContextToolRegistrationState: ContextToolRegistrationStateReader,
|
|
753
|
+
) {
|
|
754
|
+
const resolved = contextOperation(dependencies.loadConfig, ctx);
|
|
755
|
+
if (resolved.invalidFeatures.some((feature) => ["context", "compatibility", "diagnostics"].includes(feature))) return { cancel: true };
|
|
756
|
+
const config = resolved.config.compaction;
|
|
757
|
+
if (!config.enabled) {
|
|
758
|
+
return undefined;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
writeDebugArtifact(
|
|
762
|
+
"compaction-event",
|
|
763
|
+
{
|
|
764
|
+
event: "session_before_compact",
|
|
765
|
+
customInstructions: event.customInstructions,
|
|
766
|
+
preparation: {
|
|
767
|
+
tokensBefore: event.preparation.tokensBefore,
|
|
768
|
+
firstKeptEntryId: event.preparation.firstKeptEntryId,
|
|
769
|
+
previousSummaryPresent: Boolean(event.preparation.previousSummary),
|
|
770
|
+
messagesToSummarizeCount: event.preparation.messagesToSummarize.length,
|
|
771
|
+
turnPrefixMessagesCount: event.preparation.turnPrefixMessages.length,
|
|
772
|
+
},
|
|
773
|
+
},
|
|
774
|
+
config,
|
|
775
|
+
ctx,
|
|
776
|
+
);
|
|
777
|
+
|
|
778
|
+
if (event.signal.aborted) {
|
|
779
|
+
return { cancel: true };
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// Capability changes do not retire the persisted Remote session. Resolve its
|
|
783
|
+
// branch boundary before deciding whether any compactor may see this input.
|
|
784
|
+
if (config.contextManagement === "remote") {
|
|
785
|
+
try {
|
|
786
|
+
dependencies.contextWindows.synchronize(ctx);
|
|
787
|
+
} catch {
|
|
788
|
+
notifyRemoteContextFailure(ctx, "malformed-window-state");
|
|
789
|
+
return { cancel: true };
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// Active and inactive covered Codex consumers retain their existing routing.
|
|
794
|
+
const contextModel = await resolveContextCapabilityModel(ctx);
|
|
795
|
+
if (isCodexContextModel(contextModel, config)) {
|
|
796
|
+
if (await remoteContextActive(ctx, config, contextModel)) {
|
|
797
|
+
try {
|
|
798
|
+
if (event.reason === "manual" && dependencies.manualCompact && !dependencies.manualCompact.isMaintenance) {
|
|
799
|
+
await dependencies.manualCompact.request(event, ctx, resolved);
|
|
800
|
+
return { cancel: true };
|
|
801
|
+
}
|
|
802
|
+
return dependencies.contextWindows.prepareCompaction(event);
|
|
803
|
+
} catch {
|
|
804
|
+
notifyRemoteContextFailure(ctx, "malformed-window-state");
|
|
805
|
+
return { cancel: true };
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
const registrationState = getContextToolRegistrationState();
|
|
810
|
+
const failureReason = contextManagementFailureReason(registrationState);
|
|
811
|
+
if (failureReason !== "context-tools-excluded") {
|
|
812
|
+
notifyRemoteContextFailure(ctx, failureReason);
|
|
813
|
+
}
|
|
814
|
+
return runUnavailableContextCompaction(
|
|
815
|
+
event,
|
|
816
|
+
ctx,
|
|
817
|
+
dependencies,
|
|
818
|
+
resolved,
|
|
819
|
+
config,
|
|
820
|
+
registrationState,
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
if (config.contextManagement === "remote") {
|
|
825
|
+
try {
|
|
826
|
+
const detached = dependencies.contextWindows.prepareDetachedCompaction(event);
|
|
827
|
+
// A local consumer cannot summarize history owned by a Remote window,
|
|
828
|
+
// even through a configured Luna producer or Pi's default compactor.
|
|
829
|
+
if (detached) return detached;
|
|
830
|
+
} catch {
|
|
831
|
+
notifyRemoteContextFailure(ctx, "malformed-window-state");
|
|
832
|
+
return { cancel: true };
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// Resolve producer protocol policy from this operation's document, not a later disk read.
|
|
837
|
+
if (resolved.format === "v2" && config.remoteCompactModel) {
|
|
838
|
+
const producer = parseModelSpec(config.remoteCompactModel);
|
|
839
|
+
if (producer) {
|
|
840
|
+
const producerPolicy = resolveToolkitConfig(resolved.snapshot, { provider: producer.provider, id: producer.modelId });
|
|
841
|
+
notifyConfigIssues(ctx, producerPolicy);
|
|
842
|
+
if (producerPolicy.invalidFeatures.includes("compatibility")) return { cancel: true };
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
// Branch 1: Responses-family APIs use remote_compaction_v2 on the normal Responses stream.
|
|
846
|
+
let remoteAttempted = false;
|
|
847
|
+
const resolution = await resolveRemoteCompactionExecution(
|
|
848
|
+
ctx,
|
|
849
|
+
{
|
|
850
|
+
enabled: config.enabled,
|
|
851
|
+
responsesApis: config.responsesApis,
|
|
852
|
+
codexGatewayModels: compactGatewayModels(resolved),
|
|
853
|
+
},
|
|
854
|
+
config.remoteCompactModel,
|
|
855
|
+
);
|
|
856
|
+
if (resolution.ok) {
|
|
857
|
+
remoteAttempted = true;
|
|
858
|
+
const responsesOutcome = await runResponsesNativeCompact(
|
|
859
|
+
event,
|
|
860
|
+
ctx,
|
|
861
|
+
config,
|
|
862
|
+
resolution.execution,
|
|
863
|
+
dependencies.remoteCompact,
|
|
864
|
+
dependencies.projectCompactionContext,
|
|
865
|
+
dependencies.standaloneCompact,
|
|
866
|
+
);
|
|
867
|
+
if (responsesOutcome.outcome === "success") {
|
|
868
|
+
return { compaction: responsesOutcome.compaction };
|
|
869
|
+
}
|
|
870
|
+
if (responsesOutcome.outcome === "aborted") {
|
|
871
|
+
return { cancel: true };
|
|
872
|
+
}
|
|
873
|
+
if (responsesOutcome.outcome === "unprojected-input") {
|
|
874
|
+
const message = responsesOutcome.reason === COMPACTION_CHECKPOINT_CONTEXT_EDITED
|
|
875
|
+
? "A context edit changed history sealed in the opaque checkpoint. Start a new session or navigate before that checkpoint; its encrypted content cannot be selectively rewritten."
|
|
876
|
+
: responsesOutcome.reason === COMPACTION_PROJECTION_UNAVAILABLE
|
|
877
|
+
? "Pi's ordered context-hook projection is unavailable; Remote V2 was not sent raw session history."
|
|
878
|
+
: responsesOutcome.reason === COMPACTION_SESSION_CONTEXT_UNAVAILABLE
|
|
879
|
+
? "Pi did not provide the current session context required by its context-hook projection."
|
|
880
|
+
: responsesOutcome.reason === COMPACTION_PROJECTED_CONTEXT_UNAVAILABLE
|
|
881
|
+
? "Pi's projected context did not include the current compaction summary anchor."
|
|
882
|
+
: "The latest opaque checkpoint has no marker for the configured Remote V2 context source, or was created in the other mode.";
|
|
883
|
+
writeDebugArtifact(
|
|
884
|
+
"compaction-event",
|
|
885
|
+
{
|
|
886
|
+
event: "session_before_compact.remote-v2-unprojected-input",
|
|
887
|
+
reason: responsesOutcome.reason,
|
|
888
|
+
contextSource: config.remoteV2ContextSource,
|
|
889
|
+
inputProvenance: getRemoteV2InputProvenance(config.remoteV2ContextSource),
|
|
890
|
+
piContextHookPatch: dependencies.piContextHookPatch,
|
|
891
|
+
message,
|
|
892
|
+
},
|
|
893
|
+
config,
|
|
894
|
+
ctx,
|
|
895
|
+
);
|
|
896
|
+
notifyWarning(
|
|
897
|
+
ctx,
|
|
898
|
+
`Remote V2 compaction cancelled (${responsesOutcome.reason}); ${message} The existing checkpoint and session history were left unchanged.`,
|
|
899
|
+
);
|
|
900
|
+
return { cancel: true };
|
|
901
|
+
}
|
|
902
|
+
// Only an actual exhausted remote attempt warns, never an input/routing skip.
|
|
903
|
+
if (event.signal?.aborted) return { cancel: true };
|
|
904
|
+
if (responsesOutcome.remoteFailure) {
|
|
905
|
+
const failure = responsesOutcome.remoteFailure;
|
|
906
|
+
writeDebugArtifact("compaction-event", {
|
|
907
|
+
event: "session_before_compact.remote-to-native-summary",
|
|
908
|
+
protocol: responsesOutcome.protocol,
|
|
909
|
+
...failure,
|
|
910
|
+
nextStep: "native-summary",
|
|
911
|
+
}, config, ctx);
|
|
912
|
+
notifyWarning(ctx, `Remote compaction failed (${failure.reason}${failure.status ? `, HTTP ${failure.status}` : ""}${failure.errorCode ? `, ${failure.errorCode}` : ""}); continuing with a native text summary. No encrypted remote checkpoint was produced.${failure.errorMessage ? ` ${failure.errorMessage}` : ""}`);
|
|
913
|
+
}
|
|
914
|
+
} else {
|
|
915
|
+
writeDebugArtifact(
|
|
916
|
+
"compaction-event",
|
|
917
|
+
{
|
|
918
|
+
event: "session_before_compact.remote-v2-unavailable",
|
|
919
|
+
reason: resolution.reason,
|
|
920
|
+
provider: resolution.provider,
|
|
921
|
+
api: resolution.api,
|
|
922
|
+
model: resolution.model,
|
|
923
|
+
baseUrl: resolution.baseUrl,
|
|
924
|
+
modelSpec: resolution.modelSpec,
|
|
925
|
+
errorMessage: resolution.errorMessage,
|
|
926
|
+
},
|
|
927
|
+
config,
|
|
928
|
+
ctx,
|
|
929
|
+
);
|
|
930
|
+
const expectedConsumerCapabilityMiss =
|
|
931
|
+
resolution.reason === "unsupported-api" && resolution.modelSpec === undefined;
|
|
932
|
+
if (config.remoteCompactModel && !expectedConsumerCapabilityMiss) {
|
|
933
|
+
notifyWarning(
|
|
934
|
+
ctx,
|
|
935
|
+
`remote compaction model "${config.remoteCompactModel}" unusable (${resolution.reason}); using the native fallback chain`,
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
// Branch 2: run pi's native compaction method. A failed remote request is compacted by the
|
|
941
|
+
// remote producer itself; a model that cannot use remote v2 at all uses nativeFallback.model.
|
|
942
|
+
const fallbackModelSpec = remoteAttempted ? config.remoteCompactModel : config.nativeFallback.model;
|
|
943
|
+
const fallback = await dependencies.nativeFallback({ ctx, event, config, modelSpec: fallbackModelSpec });
|
|
944
|
+
if (fallback.ok) {
|
|
945
|
+
if (ctx.hasUI) {
|
|
946
|
+
ctx.ui.notify(
|
|
947
|
+
`${COMPACTION_EXTENSION_ID}: compacted with ${fallback.model.provider}/${fallback.model.id} (native method)`,
|
|
948
|
+
"info",
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
writeDebugArtifact(
|
|
952
|
+
"compaction-event",
|
|
953
|
+
{
|
|
954
|
+
event: "session_before_compact.fallback-success",
|
|
955
|
+
model: fallback.model,
|
|
956
|
+
},
|
|
957
|
+
config,
|
|
958
|
+
ctx,
|
|
959
|
+
);
|
|
960
|
+
return { compaction: fallback.result };
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
if (fallback.reason === "aborted") {
|
|
964
|
+
return { cancel: true };
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
writeDebugArtifact(
|
|
968
|
+
"compaction-event",
|
|
969
|
+
{
|
|
970
|
+
event: "session_before_compact.fallback-skip",
|
|
971
|
+
reason: fallback.reason,
|
|
972
|
+
modelSpec: fallback.modelSpec,
|
|
973
|
+
errorMessage: fallback.errorMessage,
|
|
974
|
+
},
|
|
975
|
+
config,
|
|
976
|
+
ctx,
|
|
977
|
+
);
|
|
978
|
+
|
|
979
|
+
// Intentional pi-default paths: feature disabled, nothing configured, or it matches the current one.
|
|
980
|
+
const intentionalSkip =
|
|
981
|
+
fallback.reason === "disabled" ||
|
|
982
|
+
fallback.reason === "no-model-configured" ||
|
|
983
|
+
fallback.reason === "same-as-current-model";
|
|
984
|
+
if (!intentionalSkip) {
|
|
985
|
+
// An explicit summary model is authoritative: the active model may be
|
|
986
|
+
// unable to summarize at all. Preserve context on failure, never reroute.
|
|
987
|
+
notifyWarning(
|
|
988
|
+
ctx,
|
|
989
|
+
`compaction model "${fallback.modelSpec ?? fallbackModelSpec}" failed (${fallback.reason}${fallback.errorMessage ? `: ${fallback.errorMessage}` : ""}); compaction cancelled, context preserved; not switching to the current model`,
|
|
990
|
+
);
|
|
991
|
+
return { cancel: true };
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
// Branch 3: pi's default native compaction with the current model.
|
|
995
|
+
return undefined;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
function rejectInheritedCheckpointReplay(
|
|
999
|
+
ctx: ExtensionContext,
|
|
1000
|
+
config: CompactionConfig,
|
|
1001
|
+
compactionEntryId: string,
|
|
1002
|
+
reason: string,
|
|
1003
|
+
): undefined {
|
|
1004
|
+
writeReplayFailureArtifact({ reason, compactionEntryId }, config, ctx);
|
|
1005
|
+
reportPiContextHookFailure(ctx, `replay-failed:${reason}`);
|
|
1006
|
+
if (ctx.hasUI) ctx.ui.notify(`${COMPACTION_EXTENSION_ID}: inherited checkpoint replay failed (${reason}); request aborted`, "error");
|
|
1007
|
+
ctx.abort();
|
|
1008
|
+
return undefined;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
async function handleContextInternal(
|
|
1012
|
+
event: ContextWithSystemEvent,
|
|
1013
|
+
ctx: ExtensionContext,
|
|
1014
|
+
pi: ExtensionAPI,
|
|
1015
|
+
loadConfig: typeof loadToolkitConfig,
|
|
1016
|
+
contextWindows: CodexContextWindowManager,
|
|
1017
|
+
remoteContextActive: RemoteContextActive,
|
|
1018
|
+
getContextToolRegistrationState: ContextToolRegistrationStateReader,
|
|
1019
|
+
) {
|
|
1020
|
+
const resolved = contextOperation(loadConfig, ctx);
|
|
1021
|
+
requireContextPolicy(resolved, ctx);
|
|
1022
|
+
const config = resolved.config.compaction;
|
|
1023
|
+
if (!config.enabled) {
|
|
1024
|
+
const visibleMessages = contextWindows.project(event.messages, "off");
|
|
1025
|
+
return visibleMessages.length === event.messages.length && visibleMessages.every((message, index) => message === event.messages[index])
|
|
1026
|
+
? undefined
|
|
1027
|
+
: { messages: visibleMessages };
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
let localMessages: AgentMessage[] | undefined;
|
|
1031
|
+
let remoteReplayProjection = false;
|
|
1032
|
+
if (config.contextManagement === "remote") {
|
|
1033
|
+
const contextModel = await resolveContextCapabilityModel(ctx);
|
|
1034
|
+
try {
|
|
1035
|
+
contextWindows.synchronize(ctx);
|
|
1036
|
+
} catch {
|
|
1037
|
+
notifyRemoteContextFailure(ctx, "malformed-window-state");
|
|
1038
|
+
reportPiContextHookFailure(ctx, "malformed-window-state");
|
|
1039
|
+
ctx.abort();
|
|
1040
|
+
return undefined;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
const remoteActive = await remoteContextActive(ctx, config, contextModel);
|
|
1044
|
+
if (remoteActive) {
|
|
1045
|
+
try {
|
|
1046
|
+
// Measure the provider-visible current window, not Pi's durable transcript.
|
|
1047
|
+
// The latter still contains retired remote windows and can be inflated by
|
|
1048
|
+
// an upstream error even though this request only carries the live window.
|
|
1049
|
+
let projected = contextWindows.project(event.messages, "remote", false);
|
|
1050
|
+
// Projection may observe the exact queued rollover before Pi persists
|
|
1051
|
+
// it. Decide replay ownership from this request, not the old branch.
|
|
1052
|
+
const ownsWindow = contextWindows.hasEffectiveWindow() || !contextWindows.currentIdentity();
|
|
1053
|
+
if (ownsWindow) projected = contextWindows.project(projected, "remote");
|
|
1054
|
+
contextWindows.recordBudget(
|
|
1055
|
+
pi,
|
|
1056
|
+
ctx,
|
|
1057
|
+
true,
|
|
1058
|
+
config.contextReminderThresholdPercent,
|
|
1059
|
+
);
|
|
1060
|
+
reportProjectionDiagnostics(contextWindows, config, ctx);
|
|
1061
|
+
if (ownsWindow) {
|
|
1062
|
+
return projected.length === event.messages.length && projected.every((message, index) => message === event.messages[index])
|
|
1063
|
+
? undefined
|
|
1064
|
+
: { messages: projected };
|
|
1065
|
+
}
|
|
1066
|
+
localMessages = projected;
|
|
1067
|
+
remoteReplayProjection = true;
|
|
1068
|
+
} catch (error) {
|
|
1069
|
+
notifyRemoteContextFailure(ctx, "malformed-window-state");
|
|
1070
|
+
reportPiContextHookFailure(ctx, "malformed-window-state");
|
|
1071
|
+
ctx.abort();
|
|
1072
|
+
return undefined;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
// An unavailable covered runtime must not silently become an ordinary
|
|
1077
|
+
// replay consumer. Explicit host exclusion remains the one intentional
|
|
1078
|
+
// downgrade; active Remote can replay a newer inherited checkpoint.
|
|
1079
|
+
if (!remoteActive && isCodexContextModel(contextModel, config)) {
|
|
1080
|
+
const registrationState = getContextToolRegistrationState();
|
|
1081
|
+
if (isExcludedGatewayContext(contextModel, config, registrationState)) {
|
|
1082
|
+
const visibleMessages = contextWindows.project(event.messages, "off");
|
|
1083
|
+
return visibleMessages.length === event.messages.length && visibleMessages.every((message, index) => message === event.messages[index])
|
|
1084
|
+
? undefined
|
|
1085
|
+
: { messages: visibleMessages };
|
|
1086
|
+
}
|
|
1087
|
+
if (isCodexGatewayModel(contextModel, config.gatewayContextModels)) {
|
|
1088
|
+
const reason = contextManagementFailureReason(registrationState);
|
|
1089
|
+
notifyRemoteContextFailure(ctx, reason);
|
|
1090
|
+
reportPiContextHookFailure(ctx, reason);
|
|
1091
|
+
ctx.abort();
|
|
1092
|
+
return undefined;
|
|
1093
|
+
}
|
|
1094
|
+
const visibleMessages = contextWindows.project(event.messages, "off");
|
|
1095
|
+
return visibleMessages.length === event.messages.length && visibleMessages.every((message, index) => message === event.messages[index])
|
|
1096
|
+
? undefined
|
|
1097
|
+
: { messages: visibleMessages };
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
if (!remoteActive) {
|
|
1101
|
+
try {
|
|
1102
|
+
const projected = contextWindows.project(event.messages, "local");
|
|
1103
|
+
localMessages = projected;
|
|
1104
|
+
if (contextWindows.hasEffectiveWindow()) {
|
|
1105
|
+
// This boundary owns earlier history, including any older Remote V2
|
|
1106
|
+
// checkpoint. A local request must not replay that history back in.
|
|
1107
|
+
return projected.length === event.messages.length && projected.every((message, index) => message === event.messages[index])
|
|
1108
|
+
? undefined
|
|
1109
|
+
: { messages: projected };
|
|
1110
|
+
}
|
|
1111
|
+
} catch (error) {
|
|
1112
|
+
notifyRemoteContextFailure(ctx, "malformed-window-state");
|
|
1113
|
+
reportPiContextHookFailure(ctx, error instanceof Error ? error.message : String(error));
|
|
1114
|
+
ctx.abort();
|
|
1115
|
+
return undefined;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
// Without an effective Remote boundary, the latest checkpoint owns replay.
|
|
1121
|
+
// This also covers a newer checkpoint superseding a historical window.
|
|
1122
|
+
const visibleMessages = localMessages ?? contextWindows.project(event.messages, "off");
|
|
1123
|
+
const finishProjection = (messages: AgentMessage[]) => {
|
|
1124
|
+
// Compare canonical retained copies before Remote encrypted-output encoding.
|
|
1125
|
+
const projected = remoteReplayProjection ? contextWindows.project(messages, "remote") : messages;
|
|
1126
|
+
return projected.length === event.messages.length && projected.every((message, index) => message === event.messages[index])
|
|
1127
|
+
? undefined : { messages: projected };
|
|
1128
|
+
};
|
|
1129
|
+
const replayEvent = visibleMessages === event.messages ? event : { ...event, messages: visibleMessages };
|
|
1130
|
+
const branchEntries = ctx.sessionManager.getBranch();
|
|
1131
|
+
const inherited = config.contextManagement === "remote" && contextWindows.hasSupersededWindow()
|
|
1132
|
+
? resolveLatestNativeCompactionEntry(branchEntries) : undefined;
|
|
1133
|
+
const inheritedCheckpoint = inherited?.ok ? inherited.entry : undefined;
|
|
1134
|
+
const resolution = await resolveNativeCompactionEnvironment(ctx, {
|
|
1135
|
+
enabled: config.enabled,
|
|
1136
|
+
// Disabling checkpoint generation cannot turn an inherited opaque
|
|
1137
|
+
// checkpoint into a usable text summary. Replay checks transport support.
|
|
1138
|
+
responsesApis: inheritedCheckpoint ? undefined : config.responsesApis,
|
|
1139
|
+
codexGatewayModels: compactGatewayModels(resolved),
|
|
1140
|
+
});
|
|
1141
|
+
if (!resolution.ok) {
|
|
1142
|
+
return inheritedCheckpoint
|
|
1143
|
+
? rejectInheritedCheckpointReplay(ctx, config, inheritedCheckpoint.id, resolution.reason)
|
|
1144
|
+
: finishProjection(visibleMessages);
|
|
1145
|
+
}
|
|
1146
|
+
const latest = resolveLatestNativeCompactionEntry(branchEntries, { baseUrl: resolution.runtime.baseUrl });
|
|
1147
|
+
if (!latest.ok) {
|
|
1148
|
+
return inheritedCheckpoint
|
|
1149
|
+
? rejectInheritedCheckpointReplay(ctx, config, inheritedCheckpoint.id, latest.reason)
|
|
1150
|
+
: finishProjection(visibleMessages);
|
|
1151
|
+
}
|
|
1152
|
+
const result = removeNativeCompactionRetainedMessages({
|
|
1153
|
+
messages: replayEvent.messages,
|
|
1154
|
+
branchEntries,
|
|
1155
|
+
compactionEntry: latest.entry,
|
|
1156
|
+
expectedInputProvenance: getRemoteV2InputProvenance(config.remoteV2ContextSource),
|
|
1157
|
+
});
|
|
1158
|
+
if (!result.ok) {
|
|
1159
|
+
writeReplayFailureArtifact({ reason: result.reason, compactionEntryId: latest.entry.id }, config, ctx);
|
|
1160
|
+
if (ctx.hasUI) ctx.ui.notify(`${COMPACTION_EXTENSION_ID}: replay failed (${result.reason}); request aborted`, "error");
|
|
1161
|
+
reportPiContextHookFailure(ctx, `replay-failed:${result.reason}`);
|
|
1162
|
+
ctx.abort();
|
|
1163
|
+
return undefined;
|
|
1164
|
+
}
|
|
1165
|
+
return finishProjection(result.messages);
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/**
|
|
1169
|
+
* Pi's emitContext() catches handler exceptions. Re-throw as usual for Pi, but
|
|
1170
|
+
* also report the failure so the compaction projector cannot mistake the
|
|
1171
|
+
* caught exception for a successful projection.
|
|
1172
|
+
*/
|
|
1173
|
+
async function handleContext(
|
|
1174
|
+
event: ContextWithSystemEvent,
|
|
1175
|
+
ctx: ExtensionContext,
|
|
1176
|
+
pi: ExtensionAPI,
|
|
1177
|
+
loadConfig: typeof loadToolkitConfig,
|
|
1178
|
+
contextWindows: CodexContextWindowManager,
|
|
1179
|
+
remoteContextActive: RemoteContextActive,
|
|
1180
|
+
getContextToolRegistrationState: ContextToolRegistrationStateReader,
|
|
1181
|
+
) {
|
|
1182
|
+
try {
|
|
1183
|
+
return await handleContextInternal(event, ctx, pi, loadConfig, contextWindows, remoteContextActive, getContextToolRegistrationState);
|
|
1184
|
+
} catch (error) {
|
|
1185
|
+
reportPiContextHookFailure(ctx, error instanceof Error ? error.message : String(error));
|
|
1186
|
+
throw error;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
async function handleBeforeProviderRequest(
|
|
1191
|
+
event: BeforeProviderRequestEvent,
|
|
1192
|
+
ctx: ExtensionContext,
|
|
1193
|
+
loadConfig: typeof loadToolkitConfig,
|
|
1194
|
+
contextWindows: CodexContextWindowManager,
|
|
1195
|
+
remoteContextActive: RemoteContextActive,
|
|
1196
|
+
getContextToolRegistrationState: ContextToolRegistrationStateReader,
|
|
1197
|
+
) {
|
|
1198
|
+
const resolved = contextOperation(loadConfig, ctx);
|
|
1199
|
+
requireContextPolicy(resolved, ctx);
|
|
1200
|
+
const toolkitConfig = resolved.config;
|
|
1201
|
+
const config = toolkitConfig.compaction;
|
|
1202
|
+
if (!config.enabled) {
|
|
1203
|
+
return undefined;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
const contextModel = config.contextManagement === "remote"
|
|
1207
|
+
? await resolveContextCapabilityModel(ctx)
|
|
1208
|
+
: undefined;
|
|
1209
|
+
if (config.contextManagement === "remote") {
|
|
1210
|
+
try {
|
|
1211
|
+
contextWindows.synchronize(ctx);
|
|
1212
|
+
} catch {
|
|
1213
|
+
notifyRemoteContextFailure(ctx, "malformed-window-state");
|
|
1214
|
+
ctx.abort();
|
|
1215
|
+
return undefined;
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
const remoteActive = config.contextManagement === "remote" && await remoteContextActive(ctx, config, contextModel);
|
|
1219
|
+
let remotePayload: unknown;
|
|
1220
|
+
if (remoteActive) {
|
|
1221
|
+
try {
|
|
1222
|
+
remotePayload = contextWindows.rewritePayload(event.payload, ctx);
|
|
1223
|
+
if (contextWindows.hasEffectiveWindow() || !contextWindows.currentIdentity()) return remotePayload;
|
|
1224
|
+
event = { ...event, payload: remotePayload };
|
|
1225
|
+
} catch {
|
|
1226
|
+
notifyRemoteContextFailure(ctx, "malformed-request-state");
|
|
1227
|
+
ctx.abort();
|
|
1228
|
+
return undefined;
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
if (!remoteActive && isCodexContextModel(contextModel, config)) {
|
|
1232
|
+
const registrationState = getContextToolRegistrationState();
|
|
1233
|
+
// A readable empty registry is an intentional host exclusion. Do not
|
|
1234
|
+
// rewrite gateway identity or abort an ordinary child-session request.
|
|
1235
|
+
if (isExcludedGatewayContext(contextModel, config, registrationState)) return undefined;
|
|
1236
|
+
// Keep Codex Remote mutually exclusive with the legacy replay pipeline
|
|
1237
|
+
// when authentication or tool ownership is unavailable. Gateway traffic
|
|
1238
|
+
// cannot continue as an ordinary Responses request because that would
|
|
1239
|
+
// silently lose the required CPA session/account selection.
|
|
1240
|
+
if (isCodexGatewayModel(contextModel, config.gatewayContextModels)) {
|
|
1241
|
+
const reason = contextManagementFailureReason(registrationState);
|
|
1242
|
+
notifyRemoteContextFailure(ctx, reason);
|
|
1243
|
+
ctx.abort();
|
|
1244
|
+
}
|
|
1245
|
+
return undefined;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
// Do not reintroduce a pre-window opaque checkpoint after local projection.
|
|
1249
|
+
// Request affinity/encrypted-output rewriting is reserved for active Remote.
|
|
1250
|
+
if (config.contextManagement === "remote" && contextWindows.hasEffectiveWindow()) return undefined;
|
|
1251
|
+
|
|
1252
|
+
const branchEntries = ctx.sessionManager.getBranch();
|
|
1253
|
+
const inherited = config.contextManagement === "remote" && contextWindows.hasSupersededWindow()
|
|
1254
|
+
? resolveLatestNativeCompactionEntry(branchEntries) : undefined;
|
|
1255
|
+
const inheritedCheckpoint = inherited?.ok ? inherited.entry : undefined;
|
|
1256
|
+
const resolution = await resolveNativeCompactionEnvironment(
|
|
1257
|
+
ctx,
|
|
1258
|
+
{
|
|
1259
|
+
enabled: config.enabled,
|
|
1260
|
+
responsesApis: inheritedCheckpoint ? undefined : config.responsesApis,
|
|
1261
|
+
codexGatewayModels: compactGatewayModels(resolved),
|
|
1262
|
+
},
|
|
1263
|
+
event.payload,
|
|
1264
|
+
);
|
|
1265
|
+
if (resolution.ok === false) {
|
|
1266
|
+
if (inheritedCheckpoint) return rejectInheritedCheckpointReplay(ctx, config, inheritedCheckpoint.id, resolution.reason);
|
|
1267
|
+
writeDebugArtifact(
|
|
1268
|
+
"provider-request",
|
|
1269
|
+
{
|
|
1270
|
+
event: "before_provider_request.skip",
|
|
1271
|
+
reason: resolution.reason,
|
|
1272
|
+
provider: resolution.provider,
|
|
1273
|
+
api: resolution.api,
|
|
1274
|
+
model: resolution.model,
|
|
1275
|
+
baseUrl: resolution.baseUrl,
|
|
1276
|
+
errorMessage: resolution.errorMessage,
|
|
1277
|
+
currentModel: getCurrentModelDebugInfo(ctx),
|
|
1278
|
+
payload: event.payload,
|
|
1279
|
+
},
|
|
1280
|
+
config,
|
|
1281
|
+
ctx,
|
|
1282
|
+
);
|
|
1283
|
+
return remotePayload;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
const runtime = resolution.runtime;
|
|
1287
|
+
const payload = runtime.payload;
|
|
1288
|
+
if (!payload) {
|
|
1289
|
+
return undefined;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
// Capture compact-relevant request fields (tools, reasoning, ...) for the next
|
|
1293
|
+
// synthetic compact request using the active consumer's effective runtime identity.
|
|
1294
|
+
// This hook runs before the separate Web Search transform, so injected native search
|
|
1295
|
+
// tools are not copied into remote_compaction_v2.
|
|
1296
|
+
const webSearchRoute = resolveWebSearchRoute({ model: ctx.model, config: toolkitConfig.webSearch });
|
|
1297
|
+
rememberRequestContext(
|
|
1298
|
+
payload,
|
|
1299
|
+
{
|
|
1300
|
+
provider: runtime.provider,
|
|
1301
|
+
api: runtime.api,
|
|
1302
|
+
model: runtime.requestModel ?? runtime.model,
|
|
1303
|
+
baseUrl: runtime.baseUrl,
|
|
1304
|
+
sessionId: getSessionId(ctx),
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
excludeWebSearchTools: webSearchRoute.route === "standalone-alpha",
|
|
1308
|
+
},
|
|
1309
|
+
);
|
|
1310
|
+
|
|
1311
|
+
const latestNativeCompaction = resolveLatestNativeCompactionEntry(branchEntries, {
|
|
1312
|
+
baseUrl: runtime.baseUrl,
|
|
1313
|
+
});
|
|
1314
|
+
if (!latestNativeCompaction.ok) {
|
|
1315
|
+
if (inheritedCheckpoint) return rejectInheritedCheckpointReplay(ctx, config, inheritedCheckpoint.id, latestNativeCompaction.reason);
|
|
1316
|
+
writeDebugArtifact(
|
|
1317
|
+
"provider-request",
|
|
1318
|
+
{
|
|
1319
|
+
event: "before_provider_request.no-native-compaction",
|
|
1320
|
+
reason: latestNativeCompaction.reason,
|
|
1321
|
+
provider: runtime.provider,
|
|
1322
|
+
api: runtime.api,
|
|
1323
|
+
model: runtime.model,
|
|
1324
|
+
baseUrl: runtime.baseUrl,
|
|
1325
|
+
branchEntries: branchEntries.length,
|
|
1326
|
+
latestCompactionIndex: latestNativeCompaction.latestCompactionIndex,
|
|
1327
|
+
latestCompactionIdentity: getCompactionIdentityDebugInfo(latestNativeCompaction.latestCompaction),
|
|
1328
|
+
payload,
|
|
1329
|
+
},
|
|
1330
|
+
config,
|
|
1331
|
+
ctx,
|
|
1332
|
+
);
|
|
1333
|
+
return remotePayload;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
const latestNativeCompactionEntry = latestNativeCompaction.entry;
|
|
1337
|
+
const rewrite = rewriteResponsesPayloadWithNativeReplay({
|
|
1338
|
+
model: runtime.currentModel,
|
|
1339
|
+
payload,
|
|
1340
|
+
branchEntries,
|
|
1341
|
+
compactionEntry: latestNativeCompactionEntry,
|
|
1342
|
+
expectedInputProvenance: getRemoteV2InputProvenance(config.remoteV2ContextSource),
|
|
1343
|
+
});
|
|
1344
|
+
if (!rewrite.ok) {
|
|
1345
|
+
writeDebugArtifact(
|
|
1346
|
+
"provider-request",
|
|
1347
|
+
{
|
|
1348
|
+
event: "before_provider_request.rewrite-failed",
|
|
1349
|
+
reason: rewrite.reason,
|
|
1350
|
+
provider: runtime.provider,
|
|
1351
|
+
api: runtime.api,
|
|
1352
|
+
model: runtime.model,
|
|
1353
|
+
baseUrl: runtime.baseUrl,
|
|
1354
|
+
compactionEntryId: latestNativeCompactionEntry.id,
|
|
1355
|
+
parity: rewrite.parity,
|
|
1356
|
+
payload,
|
|
1357
|
+
},
|
|
1358
|
+
config,
|
|
1359
|
+
ctx,
|
|
1360
|
+
);
|
|
1361
|
+
|
|
1362
|
+
// Fail loud instead of letting Pi send the sentinel-only payload: the
|
|
1363
|
+
// compacted history would be silently lost while the request still succeeds.
|
|
1364
|
+
// A forced redacted failure record is written even when logProviderPayloads
|
|
1365
|
+
// is disabled so the incident is diagnosable without leaking content.
|
|
1366
|
+
writeReplayFailureArtifact(
|
|
1367
|
+
{
|
|
1368
|
+
reason: rewrite.reason,
|
|
1369
|
+
parity: rewrite.parity,
|
|
1370
|
+
compactionEntryId: latestNativeCompactionEntry.id,
|
|
1371
|
+
provider: runtime.provider,
|
|
1372
|
+
api: runtime.api,
|
|
1373
|
+
model: runtime.model,
|
|
1374
|
+
},
|
|
1375
|
+
config,
|
|
1376
|
+
ctx,
|
|
1377
|
+
);
|
|
1378
|
+
if (ctx.hasUI) {
|
|
1379
|
+
ctx.ui.notify(
|
|
1380
|
+
`${COMPACTION_EXTENSION_ID}: native compaction replay failed (${rewrite.reason}); provider request aborted`,
|
|
1381
|
+
"error",
|
|
1382
|
+
);
|
|
1383
|
+
}
|
|
1384
|
+
ctx.abort();
|
|
1385
|
+
return undefined;
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
writeDebugArtifact(
|
|
1389
|
+
"provider-request",
|
|
1390
|
+
{
|
|
1391
|
+
event: "before_provider_request.native-rewrite",
|
|
1392
|
+
provider: runtime.provider,
|
|
1393
|
+
api: runtime.api,
|
|
1394
|
+
model: runtime.model,
|
|
1395
|
+
baseUrl: runtime.baseUrl,
|
|
1396
|
+
compactionEntryId: latestNativeCompactionEntry.id,
|
|
1397
|
+
boundaryIndex: rewrite.segments.boundaryIndex,
|
|
1398
|
+
firstKeptEntryIndex: rewrite.segments.firstKeptEntryIndex,
|
|
1399
|
+
originalInputItems: payload.input.length,
|
|
1400
|
+
rewrittenInputItems: rewrite.rewrittenPayload.input.length,
|
|
1401
|
+
leadingItems: rewrite.segments.leading.length,
|
|
1402
|
+
compactionSummaryItems: rewrite.segments.compactionSummary.length,
|
|
1403
|
+
compactedItems: rewrite.segments.compactedWindow.length,
|
|
1404
|
+
postItems: rewrite.segments.post.length,
|
|
1405
|
+
payload: rewrite.rewrittenPayload,
|
|
1406
|
+
originalPayload: payload,
|
|
1407
|
+
},
|
|
1408
|
+
config,
|
|
1409
|
+
ctx,
|
|
1410
|
+
);
|
|
1411
|
+
|
|
1412
|
+
return rewrite.rewrittenPayload;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
export default function registerCompactionExtension(
|
|
1416
|
+
pi: ExtensionAPI,
|
|
1417
|
+
overrides: Partial<CompactionDependencies> = {},
|
|
1418
|
+
) {
|
|
1419
|
+
const loadConfig = overrides.loadConfig ?? loadToolkitConfig;
|
|
1420
|
+
registerToolkitConfigCommand(pi, loadConfig);
|
|
1421
|
+
const piContextHookPatch = installPiContextHookPatch();
|
|
1422
|
+
const contextWindows = overrides.contextWindows ?? new CodexContextWindowManager((ctx, signal, gatewayModels) =>
|
|
1423
|
+
loadHistoryNotesThreadHint(ctx, signal, gatewayModels)
|
|
1424
|
+
);
|
|
1425
|
+
const manualCompact = new ManagedManualCompact(pi, contextWindows);
|
|
1426
|
+
const dependencies: CompactionDependencies = {
|
|
1427
|
+
remoteCompact: executeRemoteV2Compaction,
|
|
1428
|
+
standaloneCompact: executeNativeCompaction,
|
|
1429
|
+
nativeFallback: runNativeFallbackCompaction,
|
|
1430
|
+
contextWindows,
|
|
1431
|
+
...overrides,
|
|
1432
|
+
manualCompact,
|
|
1433
|
+
loadConfig: () => manualCompact.snapshot ?? loadConfig(),
|
|
1434
|
+
piContextHookPatch: overrides.piContextHookPatch ?? piContextHookPatch,
|
|
1435
|
+
};
|
|
1436
|
+
let tools!: ReturnType<typeof registerContextManagementTools>;
|
|
1437
|
+
let contextWindowReady = false;
|
|
1438
|
+
const isContextRuntimeActive = async (
|
|
1439
|
+
ctx: ExtensionContext,
|
|
1440
|
+
config: CompactionConfig,
|
|
1441
|
+
model = ctx.model,
|
|
1442
|
+
): Promise<boolean> =>
|
|
1443
|
+
contextWindowReady && tools.isRegistered && await isRemoteContextActive(ctx, config, model);
|
|
1444
|
+
tools = registerContextManagementTools(
|
|
1445
|
+
pi,
|
|
1446
|
+
contextWindows,
|
|
1447
|
+
async (ctx) => {
|
|
1448
|
+
const resolved = contextOperation(dependencies.loadConfig, ctx);
|
|
1449
|
+
assertConfigValid(resolved, "context", "compatibility");
|
|
1450
|
+
return { active: await isContextRuntimeActive(ctx, resolved.config.compaction), gatewayModels: resolved.gatewayModelKeys };
|
|
1451
|
+
},
|
|
1452
|
+
undefined,
|
|
1453
|
+
manualCompact,
|
|
1454
|
+
);
|
|
1455
|
+
const remoteContextActive: RemoteContextActive = isContextRuntimeActive;
|
|
1456
|
+
const syncTools = async (
|
|
1457
|
+
ctx: ExtensionContext,
|
|
1458
|
+
model = ctx.model,
|
|
1459
|
+
options: { notifyWindowFailure?: boolean } = {},
|
|
1460
|
+
resolved = contextOperation(dependencies.loadConfig, ctx, model),
|
|
1461
|
+
): Promise<ContextToolSyncOutcome> => {
|
|
1462
|
+
// Until this call proves otherwise, do not let a previous session/window
|
|
1463
|
+
// identity make a failed activation look usable.
|
|
1464
|
+
contextWindowReady = false;
|
|
1465
|
+
const config = resolved.config.compaction;
|
|
1466
|
+
const eligible = !resolved.invalidFeatures.some((feature) => ["context", "compatibility"].includes(feature)) && await isRemoteContextActive(ctx, config, model);
|
|
1467
|
+
const toolSync = tools.sync(eligible);
|
|
1468
|
+
const outcome: ContextToolSyncOutcome = {
|
|
1469
|
+
eligible,
|
|
1470
|
+
toolsSynced: toolSync.synced,
|
|
1471
|
+
registrationState: toolSync.registrationState,
|
|
1472
|
+
windowInitialized: false,
|
|
1473
|
+
};
|
|
1474
|
+
if (!eligible || !toolSync.synced) return outcome;
|
|
1475
|
+
// Activation can succeed after `session_start` missed it, because Pi 0.86 may
|
|
1476
|
+
// reject the registration read while a session replacement is still binding.
|
|
1477
|
+
// The window lifecycle has to open on that later activation too, or requests
|
|
1478
|
+
// carry no window metadata and the backend never ingests the turns.
|
|
1479
|
+
try {
|
|
1480
|
+
contextWindows.ensureInitialized(pi, ctx, true);
|
|
1481
|
+
contextWindowReady = true;
|
|
1482
|
+
return { ...outcome, windowInitialized: true };
|
|
1483
|
+
} catch {
|
|
1484
|
+
// Do not leave tools exposed while the request path has no valid window
|
|
1485
|
+
// identity. The next lifecycle hook may retry once Pi is usable again.
|
|
1486
|
+
tools.sync(false);
|
|
1487
|
+
if (options.notifyWindowFailure) notifyRemoteContextFailure(ctx, "malformed-window-state");
|
|
1488
|
+
return outcome;
|
|
1489
|
+
}
|
|
1490
|
+
};
|
|
1491
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
1492
|
+
const resolved = contextOperation(dependencies.loadConfig, ctx);
|
|
1493
|
+
const syncOutcome = await syncTools(ctx, ctx.model, { notifyWindowFailure: true }, resolved);
|
|
1494
|
+
await manualCompact.recover(ctx);
|
|
1495
|
+
const active = syncOutcome.eligible && syncOutcome.toolsSynced && syncOutcome.windowInitialized;
|
|
1496
|
+
const { source } = resolved;
|
|
1497
|
+
const warnings = resolved.issues.map((issue) => `${issue.path}: ${issue.code}`);
|
|
1498
|
+
const config = resolved.config.compaction;
|
|
1499
|
+
if (resolved.invalidFeatures.length > 0) return;
|
|
1500
|
+
if (!config.enabled) return;
|
|
1501
|
+
const contextModel = await resolveContextCapabilityModel(ctx);
|
|
1502
|
+
|
|
1503
|
+
let activationReason: string | undefined;
|
|
1504
|
+
// Only models the built-in Remote Context coverage targets may activate or
|
|
1505
|
+
// warn; everything else silently runs Pi's normal compaction path.
|
|
1506
|
+
if (config.contextManagement === "remote" && isCodexContextModel(contextModel, config) && !active) {
|
|
1507
|
+
if (syncOutcome.registrationState === "excluded") {
|
|
1508
|
+
activationReason = "context-tools-excluded";
|
|
1509
|
+
} else if (syncOutcome.registrationState === "conflict") {
|
|
1510
|
+
activationReason = "tool-name-conflict";
|
|
1511
|
+
notifyRemoteContextFailure(ctx, activationReason);
|
|
1512
|
+
} else if (syncOutcome.registrationState === "unverified" || !syncOutcome.toolsSynced) {
|
|
1513
|
+
// Transient: the runtime has not published or accepted our tools yet. A
|
|
1514
|
+
// later before_agent_start re-verifies, so do not report provider failure.
|
|
1515
|
+
activationReason = "tool-registration-pending";
|
|
1516
|
+
} else if (syncOutcome.eligible && !syncOutcome.windowInitialized) {
|
|
1517
|
+
activationReason = "malformed-window-state";
|
|
1518
|
+
} else {
|
|
1519
|
+
const remoteResolution = await resolveCodexContextProvider(ctx, ctx.model, config.gatewayContextModels);
|
|
1520
|
+
activationReason = remoteResolution.ok ? "codex-context-unavailable" : remoteResolution.reason;
|
|
1521
|
+
notifyRemoteContextFailure(ctx, activationReason);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
|
|
1526
|
+
const artifactPath = writeDebugArtifact(
|
|
1527
|
+
"lifecycle",
|
|
1528
|
+
{
|
|
1529
|
+
event: "session_start",
|
|
1530
|
+
config,
|
|
1531
|
+
configSource: source,
|
|
1532
|
+
warnings,
|
|
1533
|
+
activation: {
|
|
1534
|
+
active,
|
|
1535
|
+
contextManagement: config.contextManagement,
|
|
1536
|
+
remoteV2ContextSource: config.remoteV2ContextSource,
|
|
1537
|
+
model: ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined,
|
|
1538
|
+
...(activationReason ? { reason: activationReason } : {}),
|
|
1539
|
+
},
|
|
1540
|
+
piContextHookPatch: dependencies.piContextHookPatch,
|
|
1541
|
+
},
|
|
1542
|
+
config,
|
|
1543
|
+
ctx,
|
|
1544
|
+
);
|
|
1545
|
+
|
|
1546
|
+
if (ctx.hasUI && ["info", "debug"].includes(resolved.policy.diagnostics.level) && (config.notifyOnLoad || config.debug)) {
|
|
1547
|
+
ctx.ui.notify(
|
|
1548
|
+
artifactPath
|
|
1549
|
+
? `${COMPACTION_EXTENSION_ID} loaded • debug artifacts → ${artifactPath}`
|
|
1550
|
+
: `${COMPACTION_EXTENSION_ID} loaded`,
|
|
1551
|
+
"info",
|
|
1552
|
+
);
|
|
1553
|
+
}
|
|
1554
|
+
});
|
|
1555
|
+
|
|
1556
|
+
const getContextToolRegistrationState: ContextToolRegistrationStateReader = () => tools.registrationState;
|
|
1557
|
+
pi.on("context_with_system", (event, ctx) => {
|
|
1558
|
+
manualCompact.guardRequest(ctx);
|
|
1559
|
+
return handleContext(event, ctx, pi, dependencies.loadConfig, contextWindows, remoteContextActive, getContextToolRegistrationState);
|
|
1560
|
+
});
|
|
1561
|
+
pi.on("session_before_compact", (event, ctx) => handleSessionBeforeCompact(event, ctx, dependencies, remoteContextActive, getContextToolRegistrationState));
|
|
1562
|
+
pi.on("session_compact", (_event, ctx) => contextWindows.synchronize(ctx));
|
|
1563
|
+
pi.on("session_compact_failed", async (event, ctx) => {
|
|
1564
|
+
if (event.reason === "manual" && !manualCompact.isMaintenance) await manualCompact.compactFailed(ctx);
|
|
1565
|
+
});
|
|
1566
|
+
pi.on("session_shutdown", async (_event, ctx) => {
|
|
1567
|
+
await manualCompact.shutdown(ctx);
|
|
1568
|
+
contextWindowReady = false;
|
|
1569
|
+
contextWindows.reset();
|
|
1570
|
+
tools.reset();
|
|
1571
|
+
});
|
|
1572
|
+
pi.on("agent_settled", async (_event, ctx) => {
|
|
1573
|
+
// Only the owned manual handoff restores/continues here; Pi owns normal
|
|
1574
|
+
// compaction scheduling. Never compact retired windows on model changes.
|
|
1575
|
+
await manualCompact.settled(ctx);
|
|
1576
|
+
});
|
|
1577
|
+
pi.on("model_select", async (event, ctx) => {
|
|
1578
|
+
manualCompact.modelSelected(event.model, ctx);
|
|
1579
|
+
// Switching into a covered model mid-session must open the window
|
|
1580
|
+
// lifecycle immediately: without an identity the request rewrite skips
|
|
1581
|
+
// window metadata, the backend never ingests those turns, and the first
|
|
1582
|
+
// new_context would trim pre-switch history that no history can recover.
|
|
1583
|
+
// syncTools() activates and initializes the window when the model is covered.
|
|
1584
|
+
const resolved = contextOperation(dependencies.loadConfig, ctx, event.model);
|
|
1585
|
+
await syncTools(ctx, event.model, { notifyWindowFailure: true }, resolved);
|
|
1586
|
+
});
|
|
1587
|
+
pi.on("before_agent_start", async (_event, ctx) => {
|
|
1588
|
+
await syncTools(ctx, ctx.model, { notifyWindowFailure: true });
|
|
1589
|
+
});
|
|
1590
|
+
pi.on("thinking_level_select", (event, ctx) => manualCompact.thinkingSelected(event.level, ctx));
|
|
1591
|
+
pi.on("message_start", (event) => manualCompact.messageStarted(event.message));
|
|
1592
|
+
pi.on("turn_end", (_event, ctx) => manualCompact.turnEnded(ctx));
|
|
1593
|
+
pi.on("tool_call", (event) => manualCompact.guardTool(event.toolName));
|
|
1594
|
+
pi.on("session_tree", async (_event, ctx) => {
|
|
1595
|
+
contextWindows.synchronize(ctx);
|
|
1596
|
+
await manualCompact.recover(ctx);
|
|
1597
|
+
await syncTools(ctx);
|
|
1598
|
+
});
|
|
1599
|
+
pi.on("before_provider_request", (event, ctx) => {
|
|
1600
|
+
manualCompact.guardRequest(ctx);
|
|
1601
|
+
return handleBeforeProviderRequest(event, ctx, dependencies.loadConfig, contextWindows, remoteContextActive, getContextToolRegistrationState);
|
|
1602
|
+
});
|
|
1603
|
+
pi.on("before_provider_headers", async (event, ctx) => {
|
|
1604
|
+
const resolved = contextOperation(dependencies.loadConfig, ctx);
|
|
1605
|
+
requireContextPolicy(resolved, ctx);
|
|
1606
|
+
const config = resolved.config.compaction;
|
|
1607
|
+
const contextModel = await resolveContextCapabilityModel(ctx);
|
|
1608
|
+
if (!isCodexContextModel(contextModel, config)) return;
|
|
1609
|
+
const active = await remoteContextActive(ctx, config, contextModel);
|
|
1610
|
+
if (!active) {
|
|
1611
|
+
const registrationState = getContextToolRegistrationState();
|
|
1612
|
+
if (isExcludedGatewayContext(contextModel, config, registrationState)) return;
|
|
1613
|
+
if (isCodexGatewayModel(contextModel, config.gatewayContextModels)) {
|
|
1614
|
+
const reason = contextManagementFailureReason(registrationState);
|
|
1615
|
+
notifyRemoteContextFailure(ctx, reason);
|
|
1616
|
+
ctx.abort();
|
|
1617
|
+
}
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
const provider = await resolveCodexContextProvider(ctx, ctx.model, config.gatewayContextModels);
|
|
1621
|
+
if (provider.ok && provider.provider.kind === "codex-gateway") {
|
|
1622
|
+
const sessionId = getSessionId(ctx);
|
|
1623
|
+
const gatewayHeaders = codexContextProviderHeaders(provider.provider, {
|
|
1624
|
+
sessionId,
|
|
1625
|
+
clientRequestId: sessionId,
|
|
1626
|
+
});
|
|
1627
|
+
const allowedGatewayHeaders = new Set<string>(CODEX_GATEWAY_FORWARD_HEADERS);
|
|
1628
|
+
for (const existing of Object.keys(event.headers)) {
|
|
1629
|
+
if (!allowedGatewayHeaders.has(existing.toLowerCase())) delete event.headers[existing];
|
|
1630
|
+
}
|
|
1631
|
+
// Keep any caller-supplied version without forcing a Toolkit pin.
|
|
1632
|
+
// The gateway allowlist above already permits it.
|
|
1633
|
+
for (const name of [
|
|
1634
|
+
"authorization",
|
|
1635
|
+
"originator",
|
|
1636
|
+
"user-agent",
|
|
1637
|
+
"session-id",
|
|
1638
|
+
"x-client-request-id",
|
|
1639
|
+
"x-codex-affinity-scope",
|
|
1640
|
+
"x-codex-model",
|
|
1641
|
+
]) {
|
|
1642
|
+
for (const existing of Object.keys(event.headers)) {
|
|
1643
|
+
if (existing.toLowerCase() === name) delete event.headers[existing];
|
|
1644
|
+
}
|
|
1645
|
+
const value = gatewayHeaders.get(name);
|
|
1646
|
+
if (value) event.headers[name] = value;
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
contextWindows.rewriteHeaders(event.headers, ctx);
|
|
1650
|
+
});
|
|
1651
|
+
pi.on("message_end", async (event, ctx) => {
|
|
1652
|
+
rememberPhysicalModel(ctx, event.message);
|
|
1653
|
+
const resolved = contextOperation(dependencies.loadConfig, ctx);
|
|
1654
|
+
requireContextPolicy(resolved, ctx);
|
|
1655
|
+
const config = resolved.config.compaction;
|
|
1656
|
+
const contextModel = await resolveContextCapabilityModel(ctx);
|
|
1657
|
+
if (!isCodexContextModel(contextModel, config) || !tools.isRegistered) return undefined;
|
|
1658
|
+
if (!(await remoteContextActive(ctx, config, contextModel))) return undefined;
|
|
1659
|
+
const message = routeContextNamespaceToolMessage(event.message);
|
|
1660
|
+
return message === event.message ? undefined : { message };
|
|
1661
|
+
});
|
|
1662
|
+
}
|