@datspike/pi-codex-session-fast 0.0.0-stage → 0.1.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/CONTRIBUTING.md +25 -0
- package/LICENSE +21 -0
- package/README.md +53 -2
- package/SECURITY.md +7 -0
- package/docs/adr/0001-volatile-session-priority.md +36 -0
- package/docs/adr/0002-response-mode-receipts.md +17 -0
- package/extensions/session-fast.ts +130 -0
- package/package.json +32 -4
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Before making changes
|
|
4
|
+
|
|
5
|
+
1. Install Node.js 22 or newer.
|
|
6
|
+
2. Install dependencies without running third-party scripts:
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npm ci --ignore-scripts
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
3. Run the checks:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm run check
|
|
16
|
+
npm pack --dry-run
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Changes must preserve the extension boundaries: do not change auth, transport, reasoning, or Pi core. Fast lives only in memory for the active session and must not be enabled through files, environment variables, or startup settings.
|
|
20
|
+
|
|
21
|
+
## Pull requests
|
|
22
|
+
|
|
23
|
+
Describe the behavior before and after the change, add a regression test for every new scenario, and include the result of `npm run check`. Do not commit `.runtime/`, `.task_files/`, `.pi/`, `node_modules/`, credentials, or session JSONL.
|
|
24
|
+
|
|
25
|
+
Use Conventional Commits for commit messages.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 datspike
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,54 @@
|
|
|
1
|
-
#
|
|
1
|
+
# pi-codex-session-fast
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Explicit Fast mode for one active native `openai-codex` session in Pi 1.3.0+. The extension adds `service_tier: "priority"`; the server may reject or downgrade the tier. This is not a guarantee of faster responses.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Install the local package with `pi install /absolute/path/to/pi-codex-session-fast`, then start a new Pi session or run `/reload`.
|
|
8
|
+
|
|
9
|
+
Choose any `openai-codex` model through Pi's normal model picker. The extension does not change the default model, OAuth, CLIProxyAPI, transport, or reasoning. Do not load another extension that owns `/fast` or `service_tier` at the same time.
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
- `/fast` — toggle the mode.
|
|
14
|
+
- `/fast on` — enable it explicitly.
|
|
15
|
+
- `/fast off` — disable it for future requests.
|
|
16
|
+
- `/fast status` — show the current state.
|
|
17
|
+
|
|
18
|
+
Fast can be enabled at any time: the command changes only future requests and does not modify a request that has already been sent. The command does nothing for other providers. The `priority requested` indicator means that the extension requested the tier; it does not confirm that the server applied it.
|
|
19
|
+
|
|
20
|
+
## State boundaries
|
|
21
|
+
|
|
22
|
+
Fast is disabled at startup, on `/new`, resume, fork, `/reload`, and session shutdown. Switching away from provider `openai-codex` also disables it; switching back does not re-enable it. The state survives model changes within `openai-codex` in the same session.
|
|
23
|
+
|
|
24
|
+
Normal responses, tools, retries, and compaction keep the state within the same session. New processes and subagents get a disabled instance. The extension does not use files, global preferences, environment variables, or startup flags to enable Fast. Response receipts are reporting data only and never restore Fast.
|
|
25
|
+
|
|
26
|
+
`/fast off` does not cancel a request that has already been sent or remove settings owned by another component. If another `service_tier` is present, the extension leaves the payload unchanged and disables its own mode. An external `priority` is preserved with a warning. A later hook may change the final payload; do not have multiple components own the tier.
|
|
27
|
+
|
|
28
|
+
## Models and cost
|
|
29
|
+
|
|
30
|
+
Fast is available for every model on provider `openai-codex`. There is no model ID allowlist; new models from this provider are supported automatically. Other providers are not supported.
|
|
31
|
+
|
|
32
|
+
Fast increases subscription credit usage, including banked credits. The extension does not set a spending limit. Check the current terms in the [Codex documentation](https://developers.openai.com/codex/speed).
|
|
33
|
+
|
|
34
|
+
## Development and checks
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
npm ci --ignore-scripts
|
|
38
|
+
npm run check
|
|
39
|
+
npm pack --dry-run
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
TypeScript checks and lifecycle tests do not replace live acceptance. Run real model checks separately with an explicit budget and receipts. The architecture decision is documented in `docs/adr/0001-volatile-session-priority.md`.
|
|
43
|
+
|
|
44
|
+
Private plans live in the ignored `.task_files/` directory, and local runs live in `.runtime/`. Neither is included in Git or the npm package.
|
|
45
|
+
|
|
46
|
+
## Response receipts
|
|
47
|
+
|
|
48
|
+
The extension writes metadata-only `codex-session-fast-response.v1` entries to Pi JSONL: the session, model, exact `responseId`, `requestedTier` at request-hook time, and `serverTier` when the provider reports it. Request and response text are not stored. The receipt is bound at `response.created`; later events from another response or session cannot inherit it.
|
|
49
|
+
|
|
50
|
+
`requestedTier: "priority"` means that Fast was requested, not that the server honored it. A later hook may change the payload. To match receipts with SQLite telemetry, `context-compaction` must preserve `flags_json.providerResponseId`. If an ID or receipt is missing, or receipts conflict, the report keeps the mode unknown. The change takes effect after `/reload` or a new Pi process; do not repeat paid requests just to populate history.
|
|
51
|
+
|
|
52
|
+
## Contributing and security
|
|
53
|
+
|
|
54
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for local checks and contribution boundaries. See [`SECURITY.md`](SECURITY.md) for handling vulnerabilities and sensitive data.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
Do not submit keys, tokens, `auth.json` contents, session JSONL, or other secrets in issues, pull requests, or CI logs.
|
|
4
|
+
|
|
5
|
+
If you find a vulnerability, do not disclose it publicly before it is fixed. Contact the repository owner through a private channel and include the smallest reproducible description you can provide.
|
|
6
|
+
|
|
7
|
+
The extension does not own auth or transport, does not store Fast on disk, and does not store request or response text in its metadata receipts.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Fast Without Persistent State
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted. Verified on Pi 1.3.0: commands in the real TUI, session replacement through the SDK, and limited model requests.
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
Fast is a paid server preference. Persisting a global switch could silently carry higher usage into another session. Replacing the provider to add one field would take ownership of transport and cleanup that this extension does not need.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
Use one TypeScript entrypoint with public command, lifecycle, and request hooks. Keep state in a closure keyed by the session ID. Do not add runtime dependencies, state files, CLI flags, background resources, or provider registration. Not restoring Fast is intentional protection, not a missing feature.
|
|
14
|
+
|
|
15
|
+
Enable Fast only through `/fast` on `openai-codex`, including while a turn is active; the command changes only future requests. `session_start` and `session_shutdown` reset it. Leaving `openai-codex` resets it. A request must match the current session ID, provider, and model. Do not overwrite another tier; an external `priority` remains external. The hook does not confirm the served tier.
|
|
16
|
+
|
|
17
|
+
## Alternatives
|
|
18
|
+
|
|
19
|
+
- CLIProxyAPI provider: adds tier and transport ownership, while dynamic Pi adaptation and persisted state are unnecessary here.
|
|
20
|
+
- calesennett: provides a short injector and broad compatibility, but its commands write shared settings, which is not needed here.
|
|
21
|
+
- SI-RUI-ZHANG: separates core and state carefully, but persisted preferences, startup flags, and overwriting another tier do not fit this contract.
|
|
22
|
+
- dasomji: validates payload and model and avoids overwriting a tier, but its global state file is unnecessary.
|
|
23
|
+
|
|
24
|
+
The implementation is independent. It borrows verified ideas from the reference implementations rather than copying their code. A small new implementation avoids adapting their state, storage, and provider layers.
|
|
25
|
+
|
|
26
|
+
## Consequences
|
|
27
|
+
|
|
28
|
+
Fast must be enabled again after `new`, resume, fork, reload, or restart. Compaction and ordinary turns preserve it. Subagents receive a separate disabled instance. `/fast on` and `/fast off` affect only future requests owned by this extension; the backend and other hooks may have their own settings.
|
|
29
|
+
|
|
30
|
+
## Non-goals
|
|
31
|
+
|
|
32
|
+
Changing auth, transport, reasoning, the model, the default provider, guaranteed acceleration, or subscription spending controls.
|
|
33
|
+
|
|
34
|
+
## Verification
|
|
35
|
+
|
|
36
|
+
Lifecycle tests, Jiti loading, and a real TUI check are followed by a limited model check. Unit tests do not prove backend Fast behavior.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Requested Mode Receipt for Each Response
|
|
2
|
+
|
|
3
|
+
## Decision
|
|
4
|
+
|
|
5
|
+
Store a `codex-session-fast-response.v1` entry in Pi JSONL that links the requested mode to the exact `sessionId`, `responseId`, and model. Use the existing `pi.appendEntry()` and `provider_stream_event`; do not create another database or a restorable mode switch.
|
|
6
|
+
|
|
7
|
+
The receipt stores `requestedTier` when the extension processes the request and, separately, `serverTier` when the provider reports it. Request and model text are not copied. Bind the receipt at `response.created`; terminal events `response.done`, `response.completed`, `response.failed`, and `response.incomplete` close only that same response. A new session clears unfinished bindings.
|
|
8
|
+
|
|
9
|
+
To link receipts with existing SQLite telemetry, `context-compaction` preserves `flags_json.providerResponseId`. This removes ambiguity based on timing or message order. Old rows without an ID and responses without a receipt remain unknown.
|
|
10
|
+
|
|
11
|
+
## Boundaries
|
|
12
|
+
|
|
13
|
+
Fast means that priority was requested, not that the server honored it. A later hook may change the payload. The server-returned tier does not enable Fast and does not replace the requested tier. CLIProxyAPI responses are not marked by this extension.
|
|
14
|
+
|
|
15
|
+
## Alternatives
|
|
16
|
+
|
|
17
|
+
Matching by speed, time, or whole-session state was rejected: the mode can change between adjacent requests, while retries and errors disrupt ordering. A separate mode store is unnecessary because JSONL and SQLite already exist.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
const STATUS = "codex-session-fast";
|
|
4
|
+
|
|
5
|
+
type Model = { provider: string; id: string } | undefined;
|
|
6
|
+
|
|
7
|
+
/** Any model on the native subscription provider is eligible. */
|
|
8
|
+
export function eligible(model: Model): boolean {
|
|
9
|
+
return model?.provider === "openai-codex";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Registers volatile Fast state and metadata-only response receipts; never restores Fast from disk. */
|
|
13
|
+
export default function sessionFast(pi: ExtensionAPI): void {
|
|
14
|
+
let owner: string | undefined;
|
|
15
|
+
let warned = false;
|
|
16
|
+
type Receipt = { sessionId: string; provider: string; model: string; requestedTier: string };
|
|
17
|
+
let pending: Receipt | undefined;
|
|
18
|
+
let active: { id: string; receipt: Receipt } | undefined;
|
|
19
|
+
|
|
20
|
+
function show(ctx: ExtensionContext): void {
|
|
21
|
+
if (ctx.mode === "tui") {
|
|
22
|
+
ctx.ui.setStatus(STATUS, owner && eligible(ctx.model) ? "⚡ priority requested" : undefined);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function reset(ctx: ExtensionContext): void {
|
|
26
|
+
owner = undefined;
|
|
27
|
+
warned = false;
|
|
28
|
+
show(ctx);
|
|
29
|
+
}
|
|
30
|
+
function sync(ctx: ExtensionContext): void {
|
|
31
|
+
if (owner && (owner !== ctx.sessionManager.getSessionId() || !eligible(ctx.model))) reset(ctx);
|
|
32
|
+
}
|
|
33
|
+
function notify(ctx: ExtensionContext, message: string, level: "info" | "warning" = "info"): void {
|
|
34
|
+
if (ctx.hasUI) ctx.ui.notify(message, level);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function resetSession(ctx: ExtensionContext): void {
|
|
38
|
+
pending = undefined;
|
|
39
|
+
active = undefined;
|
|
40
|
+
reset(ctx);
|
|
41
|
+
}
|
|
42
|
+
pi.on("session_start", (_event, ctx) => resetSession(ctx));
|
|
43
|
+
pi.on("session_shutdown", (_event, ctx) => resetSession(ctx));
|
|
44
|
+
pi.on("model_select", (event, ctx) => {
|
|
45
|
+
// event.model is authoritative: ctx.model can still describe the previous selection.
|
|
46
|
+
if (!eligible(event.model)) reset(ctx);
|
|
47
|
+
else show(ctx);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
pi.registerCommand("fast", {
|
|
51
|
+
description: "Fast одной Codex-сессии: /fast [on|off|status]",
|
|
52
|
+
handler: async (args, ctx) => {
|
|
53
|
+
sync(ctx);
|
|
54
|
+
const command = args.trim().toLowerCase() || "toggle";
|
|
55
|
+
if (!["on", "off", "status", "toggle"].includes(command)) {
|
|
56
|
+
notify(ctx, "Использование: /fast [on|off|status]", "warning");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (command === "status") {
|
|
60
|
+
notify(ctx, owner ? "Fast включён: priority запрошен, исполнение сервером не гарантируется."
|
|
61
|
+
: "Fast выключен: расширение не добавляет priority.");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (command === "off" || (command === "toggle" && owner)) {
|
|
65
|
+
reset(ctx);
|
|
66
|
+
notify(ctx, "Fast выключен. Уже отправленный запрос не изменён.");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (!eligible(ctx.model)) {
|
|
70
|
+
reset(ctx);
|
|
71
|
+
notify(ctx, "Fast доступен только на поддержанной модели openai-codex. Провайдер не переключён.", "warning");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
owner = ctx.sessionManager.getSessionId();
|
|
75
|
+
warned = false;
|
|
76
|
+
show(ctx);
|
|
77
|
+
notify(ctx, "Fast включён только для текущей сессии. Расход кредитов выше; сервер может не применить priority.");
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
pi.on("before_provider_request", (event, ctx) => {
|
|
82
|
+
sync(ctx);
|
|
83
|
+
pending = undefined;
|
|
84
|
+
active = undefined;
|
|
85
|
+
const payload = event.payload;
|
|
86
|
+
if (!ctx.model || !eligible(ctx.model) || !payload || typeof payload !== "object" || Array.isArray(payload)) return;
|
|
87
|
+
const body = payload as Record<string, unknown>;
|
|
88
|
+
if (body.model !== ctx.model?.id) return;
|
|
89
|
+
const requestedTier = typeof body.service_tier === "string" ? body.service_tier : "default";
|
|
90
|
+
pending = { sessionId: ctx.sessionManager.getSessionId(), provider: "openai-codex",
|
|
91
|
+
model: ctx.model.id, requestedTier };
|
|
92
|
+
if (!owner) return;
|
|
93
|
+
// Another owner may have explicit tier policy. Never silently overwrite it.
|
|
94
|
+
if ("service_tier" in body) {
|
|
95
|
+
if (body.service_tier !== "priority") {
|
|
96
|
+
reset(ctx);
|
|
97
|
+
notify(ctx, "Конфликт service_tier: Fast выключен, чужой tier не изменён.", "warning");
|
|
98
|
+
} else if (!warned) {
|
|
99
|
+
warned = true;
|
|
100
|
+
notify(ctx, "priority уже задан другим компонентом. /fast off не отменяет чужие настройки.", "warning");
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
pending.requestedTier = "priority";
|
|
105
|
+
return { ...body, service_tier: "priority" };
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
pi.on("provider_stream_event", (event, ctx) => {
|
|
109
|
+
if (event.provider !== "openai-codex" || !event.data || typeof event.data !== "object") return;
|
|
110
|
+
const frame = event.data as { type?: string; response?: { id?: string; service_tier?: string } };
|
|
111
|
+
const response = frame.response;
|
|
112
|
+
if (!response || typeof response.id !== "string") return;
|
|
113
|
+
if (frame.type === "response.created" && pending
|
|
114
|
+
&& pending.sessionId === ctx.sessionManager.getSessionId() && pending.model === event.model) {
|
|
115
|
+
active = { id: response.id, receipt: pending };
|
|
116
|
+
pending = undefined;
|
|
117
|
+
}
|
|
118
|
+
if (!active || active.id !== response.id || active.receipt.sessionId !== ctx.sessionManager.getSessionId()) return;
|
|
119
|
+
if (!["response.created", "response.done", "response.completed", "response.failed", "response.incomplete"].includes(frame.type ?? "")) return;
|
|
120
|
+
// Receipts contain no payload or model text. A later hook may still override the requested tier.
|
|
121
|
+
try {
|
|
122
|
+
pi.appendEntry("codex-session-fast-response.v1", { ...active.receipt, version: 1,
|
|
123
|
+
responseId: response.id, stage: frame.type,
|
|
124
|
+
serverTier: typeof response.service_tier === "string" ? response.service_tier : null });
|
|
125
|
+
} catch {
|
|
126
|
+
notify(ctx, "Не удалось сохранить метку Fast; режим этого ответа может остаться неизвестным в отчёте.", "warning");
|
|
127
|
+
}
|
|
128
|
+
if (frame.type !== "response.created") active = undefined;
|
|
129
|
+
});
|
|
130
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datspike/pi-codex-session-fast",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
}
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"type": "module",
|
|
8
|
+
"description": "Explicit, volatile Fast for one native Codex session",
|
|
9
|
+
"files": [
|
|
10
|
+
"extensions/session-fast.ts",
|
|
11
|
+
"README.md",
|
|
12
|
+
"CONTRIBUTING.md",
|
|
13
|
+
"SECURITY.md",
|
|
14
|
+
"docs/adr"
|
|
15
|
+
],
|
|
16
|
+
"pi": {
|
|
17
|
+
"extensions": [
|
|
18
|
+
"./extensions/session-fast.ts"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"test": "node --experimental-strip-types --test tests/*.test.mjs",
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"check": "npm run typecheck && npm test"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"@earendil-works/pi-coding-agent": ">=1.3.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@earendil-works/pi-coding-agent": "1.3.0",
|
|
31
|
+
"@types/node": "^22",
|
|
32
|
+
"typescript": "^5.9.3"
|
|
33
|
+
}
|
|
34
|
+
}
|