pi-openai-toolkit 0.22.0 → 0.22.2
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/README.md +2 -2
- package/README.zh.md +2 -2
- package/docs/configuration.md +2 -2
- package/docs/internals.md +4 -4
- package/package.json +1 -1
- package/src/codex-fast/extension.ts +2 -2
- package/src/extension-runtime.ts +74 -91
package/README.md
CHANGED
|
@@ -56,13 +56,13 @@ Set `context.remoteCompaction.model` to use a separate producer on the same effe
|
|
|
56
56
|
|
|
57
57
|
**Remote windows** require the native `openai-codex` provider with `openai-codex-responses`, or an `openai-responses` gateway implementing the protocol. Gateways require `compatibility.transport: "codex-gateway"` on the exact model entry. This setting does not add the capability to an ordinary compatible endpoint.
|
|
58
58
|
|
|
59
|
-
Change `mode` in the example above to `remote-windows`. Eligible sessions expose `new_context`, `get_context_remaining`, `history`, and `notes`. Manual `/compact [instructions]` starts a checkpoint handoff: write notes successfully, then switch windows.
|
|
59
|
+
Change `mode` in the example above to `remote-windows`. Eligible sessions expose `new_context`, `get_context_remaining`, `history`, and `notes`. Manual `/compact [instructions]` starts a checkpoint handoff: write notes successfully, then switch windows. An existing effective window boundary survives model changes and switches to `remote-compaction` or `pi`: only the current local window is used, and retired Remote Context history is not sent to the model or a compaction producer. A no-summary local boundary preserves the persisted Remote Window / Context Session, so switching back to a supported model reuses the same window instead of opening a duplicate one. The legacy `leaveManagedMode` setting is retained for config compatibility but no longer triggers bulk close-out compaction.
|
|
60
60
|
|
|
61
61
|
Remote windows use alpha endpoints that may change upstream. When switching models, the remote window remains persisted and reusable; see the [window lifecycle](docs/internals.md#rollover-lifecycle).
|
|
62
62
|
|
|
63
63
|
## Codex Fast
|
|
64
64
|
|
|
65
|
-
Fast is OFF by default.
|
|
65
|
+
Fast is OFF by default. Run `/fast` in Pi's terminal or a UI-backed RPC client such as pi-web to turn it ON, then run it again to turn it OFF. Notifications confirm changes; the status is `Fast ON`, `Fast OFF`, or `Fast N/A` for an ineligible route, with display placement controlled by the client. Print/JSON and headless commands cannot toggle Fast. No Fast configuration is needed.
|
|
66
66
|
|
|
67
67
|
Supports native Codex and recognizable GPT/Codex models on compatible Responses gateways. The selection is saved only in the current session: reload/resume and `new_context` retain it; new or forked sessions start OFF. ON requests Fast, while OFF requests Standard, overriding an existing service-tier preference on eligible conversation requests without changing reasoning effort or configuration files.
|
|
68
68
|
|
package/README.zh.md
CHANGED
|
@@ -56,13 +56,13 @@ Toolkit 只读取一个全局配置文件,即使扩展安装在项目内也一
|
|
|
56
56
|
|
|
57
57
|
**远程窗口**需要原生 `openai-codex` 提供商的 `openai-codex-responses` API,或支持相应协议的 `openai-responses` 网关。网关必须在精确模型下设置 `compatibility.transport: "codex-gateway"`;这一设置不会让普通兼容端点自动获得该能力。
|
|
58
58
|
|
|
59
|
-
将上例的 `mode` 改为 `remote-windows` 后,符合条件的会话会提供 `new_context`、`get_context_remaining`、`history` 和 `notes`。手动 `/compact [instructions]` 会发起检查点交接:先成功写入 notes
|
|
59
|
+
将上例的 `mode` 改为 `remote-windows` 后,符合条件的会话会提供 `new_context`、`get_context_remaining`、`history` 和 `notes`。手动 `/compact [instructions]` 会发起检查点交接:先成功写入 notes,再切换窗口。切换模型或改用 `remote-compaction`、`pi` 模式后,已有的有效窗口边界仍然生效:只使用当前本地窗口,旧窗口历史不会发送给新模型或 compaction 模型。扩展会用一个不生成摘要的本地边界保留已持久化的 Remote Window / Context Session;切回支持窗口的模型时会复用同一个窗口,不会重复创建。旧配置中的 `leaveManagedMode` 仍会解析,但不再触发批量 close-out 压缩。
|
|
60
60
|
|
|
61
61
|
远程窗口依赖 alpha 接口,上游可能随时调整。切换模型时远程窗口状态仍会持久化并可复用,详见[窗口生命周期](docs/internals.md#rollover-lifecycle)。
|
|
62
62
|
|
|
63
63
|
## Codex Fast
|
|
64
64
|
|
|
65
|
-
Fast
|
|
65
|
+
Fast 默认关闭。在 Pi 终端或 pi-web 等支持 UI 的 RPC 客户端中输入 `/fast` 开启,再输入一次关闭。切换结果通过通知确认;状态为 `Fast ON`、`Fast OFF`,不适用的路由为 `Fast N/A`,显示位置由客户端决定。print/JSON 和无 UI 模式不能切换 Fast。无需添加 Fast 配置。
|
|
66
66
|
|
|
67
67
|
支持原生 Codex,以及兼容 Responses 网关中可识别的 GPT/Codex 模型。开关只保存在当前会话:reload、续聊和 `new_context` 保留状态,新建或 fork 的会话默认关闭。开启时请求 Fast,关闭时请求标准档,会覆盖符合条件的对话请求中原有的服务档位,但不改变 reasoning effort 或配置文件。
|
|
68
68
|
|
package/docs/configuration.md
CHANGED
|
@@ -56,7 +56,7 @@ Place `context` under `defaults` or an exact `models` entry.
|
|
|
56
56
|
|
|
57
57
|
| Relative field | Default | Contract |
|
|
58
58
|
| --- | --- | --- |
|
|
59
|
-
| `mode` | `"remote-compaction"` | `"pi"`
|
|
59
|
+
| `mode` | `"remote-compaction"` | `"pi"` delegates ordinary context management to Pi; `"remote-compaction"` uses the eligible Responses checkpoint path; `"remote-windows"` selects Codex windows where supported. |
|
|
60
60
|
| `remoteCompaction.model` | `null` | Exact producer reference; `null` uses the active model. In `remote-compaction`, it selects the synthetic checkpoint producer on the same effective base URL. In active `remote-windows`, manual `/compact` temporarily selects it for notes checkpoint duty on the same backend/account. |
|
|
61
61
|
| `remoteCompaction.inputSource` | `"legacy"` | `"legacy"` preserves session/raw-branch input; `"pi-context-hook"` opts into Pi's ordered context projection. Checkpoint provenance must match. |
|
|
62
62
|
| `remoteCompaction.allowContinuityBreak` | `false` | Allow restarting from Pi context after a foreign compaction entry. It does not make malformed opaque checkpoints replayable. |
|
|
@@ -71,7 +71,7 @@ Ordinary remote compaction tries V2 first. One automatic standalone `/v1/respons
|
|
|
71
71
|
|
|
72
72
|
Standalone success preserves the full canonical output window, including retained items, rather than treating retained assistant text as a summary. When actual remote attempts are exhausted, Toolkit warns that no encrypted checkpoint was produced and starts the existing native text-summary chain: the configured remote producer after a remote attempt, or `nativeFallback.model` when remote compaction is unavailable. `nativeFallback.enabled: false` disables only the configured-model tier; disabled/unset/same-model selections still permit Pi's default summary. Explicit summary-model failures and checkpoint-integrity failures cancel. OpenAI subscription-sharing OAuth can reject compaction; separately authenticating native Codex is a different route, not a way to reuse that grant. See the [wire contract](internals.md#remote-compaction-v2-wire-contract).
|
|
73
73
|
|
|
74
|
-
Native Codex eligibility derives from the actual Pi provider/API. Gateways additionally need the exact transport opt-in below.
|
|
74
|
+
Native Codex eligibility derives from the actual Pi provider/API. Gateways additionally need the exact transport opt-in below. An existing effective Remote Window boundary survives model and context-mode changes, including `remote-compaction`, `pi`, and legacy disabled/off settings. A non-window consumer receives only the current local window; retired Remote Context history is excluded from live requests and compaction producers. This local safety check does not activate Remote tools or protocol support. A no-summary boundary preserves the marker for later reuse. A configured window model whose activation fails does not silently receive a summary fallback. Normal `new_context` keeps its persisted-notes, duplicate and cooldown gates. Synthetic Remote V2 producer/consumer identity and checkpoint provenance are unchanged. See [Toolkit internals](internals.md).
|
|
75
75
|
|
|
76
76
|
In active `remote-windows`, `/compact [instructions]` starts checkpoint duty rather than producing a summary. The selected model must authenticate to the same native backend/account or gateway credential/affinity domain, fit the projected window plus its output reserve, and retain the required permitted context tools. Invalid or unsuitable explicit targets are refused without switching to a fallback model. The entire handoff uses the initiating context-policy snapshot; no config or global Pi defaults are rewritten.
|
|
77
77
|
|
package/docs/internals.md
CHANGED
|
@@ -29,7 +29,7 @@ Selected invalid policy blocks dependent operations; unrelated model errors rema
|
|
|
29
29
|
|
|
30
30
|
### Codex Fast session control
|
|
31
31
|
|
|
32
|
-
[src/codex-fast/extension.ts](../src/codex-fast/extension.ts) registers the human-only, no-argument `/fast` toggle and an owned `ctx.ui.setStatus()` item. Default OFF requests `service_tier: "default"`; ON requests `"priority"`, the Codex Fast wire value. The status is the local request preference, not verified server acceptance or latency. Fast can increase usage/cost. No config setting, model tool, credential probe, retry, new header, reasoning-effort change or custom footer is added.
|
|
32
|
+
[src/codex-fast/extension.ts](../src/codex-fast/extension.ts) registers the human-only, no-argument `/fast` toggle and an owned `ctx.ui.setStatus()` item. The command accepts UI-backed TUI/RPC (including pi-web) and older UI contexts without `mode`; print/JSON and headless commands cannot toggle. Notifications confirm changes, while status rendering belongs to the client. Default OFF requests `service_tier: "default"`; ON requests `"priority"`, the Codex Fast wire value. The status is the local request preference, not verified server acceptance or latency. Fast can increase usage/cost. No config setting, model tool, credential probe, retry, new header, reasoning-effort change or custom footer is added.
|
|
33
33
|
|
|
34
34
|
Native `openai-codex-responses` routes qualify. Non-official HTTP(S) `openai-responses` gateways qualify for recognizable GPT/Codex IDs or an existing exact Codex gateway identity, including a resolved virtual route. Unknown opaque aliases without that identity, Azure, other APIs and ordinary `api.openai.com` API-key routes remain untouched. A capability marker is not backend entitlement.
|
|
35
35
|
|
|
@@ -37,7 +37,7 @@ Version-1 `pi-openai-toolkit:codex-fast` custom entries contain only the owning
|
|
|
37
37
|
|
|
38
38
|
The payload hook takes one selection snapshot and returns a new object only when a matching ordinary Responses request needs a tier change. Explicit OFF also overrides an existing Fast/Flex tier to Standard. Dedicated compaction shapes and malformed/foreign payloads are left unchanged. Fast loads after Web Search/image registration and before the final Astra effort pass, so its tier does not alter the pre-search cached extras used by separate search/image/compaction clients.
|
|
39
39
|
|
|
40
|
-
Native Pi and Toolkit gateway WebSocket adapters already compare non-input body fields before incremental reuse. A changed `service_tier` invalidates the incompatible `previous_response_id` delta; the existing transport/retry logic is retained. Offline official-Pi fixtures in [test/pi-codex-fast.test.ts](../test/pi-codex-fast.test.ts) cover
|
|
40
|
+
Native Pi and Toolkit gateway WebSocket adapters already compare non-input body fields before incremental reuse. A changed `service_tier` invalidates the incompatible `previous_response_id` delta; the existing transport/retry logic is retained. Offline official-Pi fixtures in [test/pi-codex-fast.test.ts](../test/pi-codex-fast.test.ts) cover TUI/RPC command/status/notifications, actual native/gateway HTTP/WebSocket bodies, saved reopen/reload, fork isolation and continuation transitions. They do not establish live Fast availability.
|
|
41
41
|
|
|
42
42
|
---
|
|
43
43
|
|
|
@@ -63,7 +63,7 @@ Coverage is never inferred from model names. A native session uses provider `ope
|
|
|
63
63
|
|
|
64
64
|
The four Toolkit context tools are `new_context`, `get_context_remaining`, `history`, and `notes`. The canonical names live in [src/context-management/tool-contract.ts](../src/context-management/tool-contract.ts). A launcher that creates a Pi session for an exact `compaction.gatewayContextModels` model should union those names into an explicit `tools` allowlist before `createAgentSession`, unless the caller selected `noTools`/`--no-tools`; an explicit `excludeTools` entry remains authoritative and is never re-added. This package does not contain the `pi-subagent` or child-session launcher source, so the helper and contract here do not claim to modify that external layer.
|
|
65
65
|
|
|
66
|
-
After registration, Pi's published registry is authoritative. Toolkit distinguishes `verified` (all four owned definitions), `excluded` (a readable registry contains none of them), `unverified` (a transient read or partial publication), and `conflict` (registration/API/ownership failure). Only `excluded` is a live-turn downgrade: `context_with_system` strips internal
|
|
66
|
+
After registration, Pi's published registry is authoritative. Toolkit distinguishes `verified` (all four owned definitions), `excluded` (a readable registry contains none of them), `unverified` (a transient read or partial publication), and `conflict` (registration/API/ownership failure). Only `excluded` is a live-turn downgrade: `context_with_system` applies the safe local projection at any effective inherited window boundary and strips internal markers, provider payload/header hooks return no-op results, and the lifecycle artifact records `context-tools-excluded`. Without an owned boundary, ordinary child requests remain unchanged apart from marker filtering. Conflicts, unreadable/partial registries, malformed window state and missing gateway identity remain fail-closed for live provider requests.
|
|
67
67
|
|
|
68
68
|
If a covered Remote Context session reaches `session_before_compact` without an active context runtime, the first compaction path is the configured `compaction.remoteCompactModel` through Remote V2. The active session model remains the checkpoint consumer; the configured remote model is only the compactor. If that model is missing/unusable, only an explicitly configured/enabled `nativeFallback.model` may run. Otherwise the hook cancels rather than silently invoking the active model or Pi's default compaction. This compaction fallback does not weaken the live-provider safety rules above.
|
|
69
69
|
|
|
@@ -110,7 +110,7 @@ State invariants the lifecycle code must keep honest:
|
|
|
110
110
|
- Pi's canonical session projection is the source of effective history. Toolkit may further restrict that projection, but never reconstruct omitted or summarized messages from the raw branch. Boundary resolution uses projected source entries and the active branch's latest compaction; an older marker retained inside a newer compaction does not authorize dropping that compaction's summary.
|
|
111
111
|
- Window identity and cleanup eligibility are separate. A persisted `codex-context-window` message restores identity after reload/fork, but its historical `trimPreviousWindow` flag is not an outstanding command. A newer native summary or opaque checkpoint supersedes old cleanup without erasing window identity. Missing or edited boundary evidence is never repaired by loading raw history.
|
|
112
112
|
- Maintenance compaction and local-consumer detachment use the same effective-boundary rules. Only a safe current boundary can produce a no-summary compaction; obsolete cleanup must not move the retained start backwards or replace a newer summary/checkpoint. Preparation alone changes no durable state, and cancellation, retry and branch navigation are resolved from current evidence rather than callback acknowledgments. User manual `/compact` still follows the managed handoff below.
|
|
113
|
-
- A non-window consumer receives only the effective current context, with the system/tool head preserved and internal
|
|
113
|
+
- An effective persisted window boundary belongs to the session, not the selected model or context mode. `context_with_system`, `before_provider_request`, and `session_before_compact` synchronize it before mode-specific exits, including `remote-compaction`, `pi`, and legacy disabled/off selections. A non-window consumer receives only the effective current context, with the current system/tool head preserved and internal markers removed; no Remote tools, metadata or affinity headers are activated by the local safety check. A pre-window opaque checkpoint cannot rehydrate retired history after local projection. When detachment is needed, the existing no-summary boundary runs before any configured, remote, native or Pi-default compaction producer. Once a later ordinary compaction owns effective context, the historical window does not suppress its summary or valid opaque replay. A genuinely new window can retire that checkpoint. Disabling opaque checkpoint generation through `responsesApis` does not disable required replay of an inherited checkpoint; an unsupported transport, authentication failure, or endpoint mismatch aborts the request instead of exposing its placeholder. The legacy `leaveManagedMode` setting remains compatible but does not start a bulk close-out from `model_select` or `agent_settled`.
|
|
114
114
|
- Native text summarization delegates prompt construction, tool-result truncation, split-turn requests and output limits to Pi's `compact()`. Toolkit does not use character-count estimates to reject the configured summary model. If its resolution, authentication or summary generation fails, compaction is cancelled with the error and context is preserved; the active model is not an implicit retry target. Disabled/unset/same-model selections retain Pi's default path. Remote-window projection still excludes retired managed history before any producer call.
|
|
115
115
|
|
|
116
116
|
---
|
package/package.json
CHANGED
|
@@ -72,8 +72,8 @@ export function registerCodexFastExtension(
|
|
|
72
72
|
pi.registerCommand("fast", {
|
|
73
73
|
description: "Toggle Codex Fast for this session (default OFF; may increase usage/cost).",
|
|
74
74
|
handler: async (args, ctx) => {
|
|
75
|
-
if (!ctx.hasUI || (ctx.mode !== undefined && ctx.mode !== "tui")) {
|
|
76
|
-
notify(ctx, "/fast requires an interactive terminal.", "warning");
|
|
75
|
+
if (!ctx.hasUI || (ctx.mode !== undefined && ctx.mode !== "tui" && ctx.mode !== "rpc")) {
|
|
76
|
+
notify(ctx, "/fast requires an interactive UI (terminal or RPC).", "warning");
|
|
77
77
|
return;
|
|
78
78
|
}
|
|
79
79
|
if (args.trim()) { notify(ctx, "Usage: /fast (toggle ON/OFF)", "warning"); return; }
|
package/src/extension-runtime.ts
CHANGED
|
@@ -754,44 +754,42 @@ async function handleSessionBeforeCompact(
|
|
|
754
754
|
const resolved = contextOperation(dependencies.loadConfig, ctx);
|
|
755
755
|
if (resolved.invalidFeatures.some((feature) => ["context", "compatibility", "diagnostics"].includes(feature))) return { cancel: true };
|
|
756
756
|
const config = resolved.config.compaction;
|
|
757
|
-
if (
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
messagesToSummarizeCount: event.preparation.messagesToSummarize.length,
|
|
771
|
-
turnPrefixMessagesCount: event.preparation.turnPrefixMessages.length,
|
|
757
|
+
if (config.enabled) {
|
|
758
|
+
writeDebugArtifact(
|
|
759
|
+
"compaction-event",
|
|
760
|
+
{
|
|
761
|
+
event: "session_before_compact",
|
|
762
|
+
customInstructions: event.customInstructions,
|
|
763
|
+
preparation: {
|
|
764
|
+
tokensBefore: event.preparation.tokensBefore,
|
|
765
|
+
firstKeptEntryId: event.preparation.firstKeptEntryId,
|
|
766
|
+
previousSummaryPresent: Boolean(event.preparation.previousSummary),
|
|
767
|
+
messagesToSummarizeCount: event.preparation.messagesToSummarize.length,
|
|
768
|
+
turnPrefixMessagesCount: event.preparation.turnPrefixMessages.length,
|
|
769
|
+
},
|
|
772
770
|
},
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
771
|
+
config,
|
|
772
|
+
ctx,
|
|
773
|
+
);
|
|
774
|
+
}
|
|
777
775
|
|
|
778
776
|
if (event.signal.aborted) {
|
|
779
777
|
return { cancel: true };
|
|
780
778
|
}
|
|
781
779
|
|
|
782
|
-
//
|
|
783
|
-
//
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
return { cancel: true };
|
|
790
|
-
}
|
|
780
|
+
// Window ownership survives model-policy changes, including Pi/disabled mode.
|
|
781
|
+
// Resolve it before any ordinary or default compactor can receive this input.
|
|
782
|
+
try {
|
|
783
|
+
dependencies.contextWindows.synchronize(ctx);
|
|
784
|
+
} catch {
|
|
785
|
+
notifyRemoteContextFailure(ctx, "malformed-window-state");
|
|
786
|
+
return { cancel: true };
|
|
791
787
|
}
|
|
792
788
|
|
|
793
789
|
// Active and inactive covered Codex consumers retain their existing routing.
|
|
794
|
-
const contextModel =
|
|
790
|
+
const contextModel = config.enabled && config.contextManagement === "remote"
|
|
791
|
+
? await resolveContextCapabilityModel(ctx)
|
|
792
|
+
: undefined;
|
|
795
793
|
if (isCodexContextModel(contextModel, config)) {
|
|
796
794
|
if (await remoteContextActive(ctx, config, contextModel)) {
|
|
797
795
|
try {
|
|
@@ -821,17 +819,16 @@ async function handleSessionBeforeCompact(
|
|
|
821
819
|
);
|
|
822
820
|
}
|
|
823
821
|
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
return { cancel: true };
|
|
833
|
-
}
|
|
822
|
+
try {
|
|
823
|
+
const detached = dependencies.contextWindows.prepareDetachedCompaction(event);
|
|
824
|
+
// A local consumer cannot summarize history owned by a Remote window,
|
|
825
|
+
// even after switching modes or disabling Toolkit compaction.
|
|
826
|
+
if (detached) return detached;
|
|
827
|
+
} catch {
|
|
828
|
+
notifyRemoteContextFailure(ctx, "malformed-window-state");
|
|
829
|
+
return { cancel: true };
|
|
834
830
|
}
|
|
831
|
+
if (!config.enabled) return undefined;
|
|
835
832
|
|
|
836
833
|
// Resolve producer protocol policy from this operation's document, not a later disk read.
|
|
837
834
|
if (resolved.format === "v2" && config.remoteCompactModel) {
|
|
@@ -1020,26 +1017,22 @@ async function handleContextInternal(
|
|
|
1020
1017
|
const resolved = contextOperation(loadConfig, ctx);
|
|
1021
1018
|
requireContextPolicy(resolved, ctx);
|
|
1022
1019
|
const config = resolved.config.compaction;
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1020
|
+
try {
|
|
1021
|
+
contextWindows.synchronize(ctx);
|
|
1022
|
+
} catch {
|
|
1023
|
+
notifyRemoteContextFailure(ctx, "malformed-window-state");
|
|
1024
|
+
reportPiContextHookFailure(ctx, "malformed-window-state");
|
|
1025
|
+
ctx.abort();
|
|
1026
|
+
return undefined;
|
|
1028
1027
|
}
|
|
1029
1028
|
|
|
1030
1029
|
let localMessages: AgentMessage[] | undefined;
|
|
1031
1030
|
let remoteReplayProjection = false;
|
|
1032
|
-
|
|
1031
|
+
// Disabled and intentionally downgraded runtimes never enter opaque replay,
|
|
1032
|
+
// but still honor a window already owned by this session.
|
|
1033
|
+
let localProjectionOnly = !config.enabled;
|
|
1034
|
+
if (config.enabled && config.contextManagement === "remote") {
|
|
1033
1035
|
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
1036
|
const remoteActive = await remoteContextActive(ctx, config, contextModel);
|
|
1044
1037
|
if (remoteActive) {
|
|
1045
1038
|
try {
|
|
@@ -1078,42 +1071,34 @@ async function handleContextInternal(
|
|
|
1078
1071
|
// downgrade; active Remote can replay a newer inherited checkpoint.
|
|
1079
1072
|
if (!remoteActive && isCodexContextModel(contextModel, config)) {
|
|
1080
1073
|
const registrationState = getContextToolRegistrationState();
|
|
1081
|
-
if (isExcludedGatewayContext(contextModel, config, registrationState)
|
|
1082
|
-
|
|
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)) {
|
|
1074
|
+
if (!isExcludedGatewayContext(contextModel, config, registrationState)
|
|
1075
|
+
&& isCodexGatewayModel(contextModel, config.gatewayContextModels)) {
|
|
1088
1076
|
const reason = contextManagementFailureReason(registrationState);
|
|
1089
1077
|
notifyRemoteContextFailure(ctx, reason);
|
|
1090
1078
|
reportPiContextHookFailure(ctx, reason);
|
|
1091
1079
|
ctx.abort();
|
|
1092
1080
|
return undefined;
|
|
1093
1081
|
}
|
|
1094
|
-
|
|
1095
|
-
return visibleMessages.length === event.messages.length && visibleMessages.every((message, index) => message === event.messages[index])
|
|
1096
|
-
? undefined
|
|
1097
|
-
: { messages: visibleMessages };
|
|
1082
|
+
localProjectionOnly = true;
|
|
1098
1083
|
}
|
|
1084
|
+
}
|
|
1099
1085
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
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;
|
|
1086
|
+
if (!remoteReplayProjection) {
|
|
1087
|
+
try {
|
|
1088
|
+
const projected = contextWindows.project(event.messages, "local");
|
|
1089
|
+
localMessages = projected;
|
|
1090
|
+
if (localProjectionOnly || contextWindows.hasEffectiveWindow()) {
|
|
1091
|
+
// A persisted boundary owns earlier history regardless of the selected
|
|
1092
|
+
// mode. Neither disabled policy nor ordinary replay may expand it.
|
|
1093
|
+
return projected.length === event.messages.length && projected.every((message, index) => message === event.messages[index])
|
|
1094
|
+
? undefined
|
|
1095
|
+
: { messages: projected };
|
|
1116
1096
|
}
|
|
1097
|
+
} catch (error) {
|
|
1098
|
+
notifyRemoteContextFailure(ctx, "malformed-window-state");
|
|
1099
|
+
reportPiContextHookFailure(ctx, error instanceof Error ? error.message : String(error));
|
|
1100
|
+
ctx.abort();
|
|
1101
|
+
return undefined;
|
|
1117
1102
|
}
|
|
1118
1103
|
}
|
|
1119
1104
|
|
|
@@ -1199,6 +1184,13 @@ async function handleBeforeProviderRequest(
|
|
|
1199
1184
|
requireContextPolicy(resolved, ctx);
|
|
1200
1185
|
const toolkitConfig = resolved.config;
|
|
1201
1186
|
const config = toolkitConfig.compaction;
|
|
1187
|
+
try {
|
|
1188
|
+
contextWindows.synchronize(ctx);
|
|
1189
|
+
} catch {
|
|
1190
|
+
notifyRemoteContextFailure(ctx, "malformed-window-state");
|
|
1191
|
+
ctx.abort();
|
|
1192
|
+
return undefined;
|
|
1193
|
+
}
|
|
1202
1194
|
if (!config.enabled) {
|
|
1203
1195
|
return undefined;
|
|
1204
1196
|
}
|
|
@@ -1206,15 +1198,6 @@ async function handleBeforeProviderRequest(
|
|
|
1206
1198
|
const contextModel = config.contextManagement === "remote"
|
|
1207
1199
|
? await resolveContextCapabilityModel(ctx)
|
|
1208
1200
|
: 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
1201
|
const remoteActive = config.contextManagement === "remote" && await remoteContextActive(ctx, config, contextModel);
|
|
1219
1202
|
let remotePayload: unknown;
|
|
1220
1203
|
if (remoteActive) {
|
|
@@ -1247,7 +1230,7 @@ async function handleBeforeProviderRequest(
|
|
|
1247
1230
|
|
|
1248
1231
|
// Do not reintroduce a pre-window opaque checkpoint after local projection.
|
|
1249
1232
|
// Request affinity/encrypted-output rewriting is reserved for active Remote.
|
|
1250
|
-
if (
|
|
1233
|
+
if (contextWindows.hasEffectiveWindow()) return undefined;
|
|
1251
1234
|
|
|
1252
1235
|
const branchEntries = ctx.sessionManager.getBranch();
|
|
1253
1236
|
const inherited = config.contextManagement === "remote" && contextWindows.hasSupersededWindow()
|