@pure01fx/dsh-openai-codex-auth 0.13.3 → 0.13.4

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/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.13.4
6
+
7
+ - Extends the replacement meter to supply the UI context-pressure projection. Estimated occupancy includes native encrypted replay while measured input remains unchanged; per-session request routes, model-window matching, and compatible sample selection isolate conversations and model switches. Projection checkpoints recompute policy-dependent views on restore, and ownership changes rebuild the single pressure registration.
8
+
5
9
  ## 0.13.3
6
10
 
7
11
  - Supplies a replacement token-meter service through the package composition patch, so Codex encrypted reasoning participates in retained-history pricing and pressure reductions after compaction without changing Harness source. Reuses ordinary host pricing, preserves fixed heuristic projections, and corrects usage anchors without double-counting reasoning output. Preserves an explicitly reported zero reasoning count so it does not trigger the missing-detail fallback.
package/README.md CHANGED
@@ -253,6 +253,32 @@ $DSH_HOME/openai-codex-auth.json
253
253
 
254
254
  `path` 的优先级高于 `dshHome`。
255
255
 
256
+ ### 关闭或恢复计量服务替代
257
+
258
+ 计量服务的选择由组合节点控制,目前不是 `openai-codex-auth` 的 `config` 开关。要保留 Codex Provider、OAuth 和 UI 功能,同时恢复 DSH 默认计量器,将以下覆盖项放到 profile 最后应用的补丁中(在本包补丁之后):
259
+
260
+ ```yaml
261
+ - id: token-meter
262
+ disabled: false
263
+
264
+ - id: codex-token-meter
265
+ disabled: true
266
+ ```
267
+
268
+ 两项必须同时设置,确保只有一个 `ctx.tokenMeter`;关闭替代后,本包的隐藏推理保留预算修正及 UI 预计占用修正都会停用。`nativeAdapter: false` 只切换 Provider 行为,不会恢复 base 的计量节点。自定义 profile 如果使用不同节点 ID,应替换上面的 ID。
269
+
270
+ 保存覆盖配置后重新启动该 profile,使服务选择在启动时生效,避免在线切换过程中出现两个计量服务同时挂载。
271
+
272
+ 要重新启用本包计量器,反向覆盖这两项并重新启动 profile:
273
+
274
+ ```yaml
275
+ - id: token-meter
276
+ disabled: true
277
+
278
+ - id: codex-token-meter
279
+ disabled: false
280
+ ```
281
+
256
282
  ### 原生 Codex transport 与组合边界
257
283
 
258
284
  本包跟踪的上游 Codex repository、commit、release 与 Catalog client version 只在 `src/upstream.ts` 维护;升级上游时先更新该文件,再刷新 fixtures、生成产物和兼容测试,避免多个散落 hash 漂移。
@@ -261,9 +287,11 @@ $DSH_HOME/openai-codex-auth.json
261
287
 
262
288
  本包提供 Codex Provider 和计量服务替代入口,不发布具体 Profile。整合包必须在挂载本插件前释放已有的 `openai-codex` route,例如从自己的 `llm-pi-ai` provider map 中移除该项;本包不会清空未知的 sibling providers。只想复用 OAuth/UI 并继续使用外部 Provider 的组合可以显式设置 `nativeAdapter: false`。从 pi-ai 切到 Native 时,旧 foreign replay 会降级为可见持久历史;反向切回 pi-ai 后,已产生 Native replay 的进行中 session 不保证可续跑,应新建 session。
263
289
 
264
- `cordis.patch.yml` 会禁用 base bundle 的 `token-meter` 节点,并挂载本包 `/token-meter` 入口作为唯一的 `ctx.tokenMeter`;无需修改 DSH 源码。自定义组合应禁用自己的默认计量服务,再挂载同一入口,不能同时运行两个计量服务。服务复用宿主的文本、图片、文件和投影实现,只对源 route 与目标 route 属于同一个活跃 Native Codex adapter 的历史补充加密推理价格;`nativeAdapter: false` 和其他模型沿用宿主价格。
290
+ `cordis.patch.yml` 会禁用 base bundle 的 `token-meter` 节点,并挂载本包 `/token-meter` 入口作为唯一的 `ctx.tokenMeter`;无需修改 DSH 源码。自定义组合应禁用自己的默认计量服务,再挂载同一入口,不能同时运行两个计量服务。服务复用宿主的文本、图片、文件计价及用量、组成投影,只对源 route 与目标 route 属于同一个活跃 Native Codex adapter 的历史补充加密推理价格;`nativeAdapter: false` 和其他模型沿用宿主价格。
291
+
292
+ 加密推理计量优先使用原始响应的 `usage.reasoningTokens`,每次响应计一次;缺少推理细分时以 `outputTokens` 作为生成推理的保守上界,连用量也没有时才用解码密文的字节数作体积估算。这些都是重放占用估计,不是精确的服务端输入计费。计量会同时修正节点 `tokens`、`surfaceTokens`、完整锚点与有符号的 `surfaceDeltaTokens`,服务端已计入的推理不会再加一次,压缩移除历史时会扣减隐藏占用;`heuristicTokens`、持久消息和服务端用量保持原语义。UI 的 `contextPressure` 由本包注册一个纯投影,复用同一隐藏推理定价函数;`pressureTokens` 仍是最近服务端实测输入,`projectedTokens` 是预计下一次请求占用,适用的加密推理会计入增长和压缩扣减。每个对话根据已提交的 `request/header` 独立选择路由,稳定的其他模型对话沿用原投影规则;模型或请求信封改变后重新估算,并在匹配的 `request/context` 到达前不显示旧模型窗口。
265
293
 
266
- 加密推理计量优先使用原始响应的 `usage.reasoningTokens`,每次响应计一次;缺少推理细分时以 `outputTokens` 作为生成推理的保守上界,连用量也没有时才用解码密文的字节数作体积估算。这些都是重放占用估计,不是精确的服务端输入计费。计量会同时修正节点 `tokens`、`surfaceTokens`、完整锚点与有符号的 `surfaceDeltaTokens`,服务端已计入的推理不会再加一次,压缩移除历史时会扣减隐藏占用;`heuristicTokens`、持久消息和服务端用量保持原语义。宿主 UI 的固定启发式投影仍由原实现提供,不包含本包的隐藏推理估计。
294
+ UI 投影的缓存保存与当前路由策略无关的每来源价格和采样总量,读取旧格式缓存会完整回放;同格式缓存读取时重新计算展示值,因此重启、分支恢复及 Native 路由归属变化不会沿用旧展示缓存。无法确认兼容性的跨 route 用量样本会使用完整估算,实测输入字段不变。路由归属变化会重建投影;宿主注册接口不主动推送空闲对话,现有订阅在下一次会话事件或快照读取时看到新值。尚未提交为请求信封的模型选择不属于该日志投影的输入。
267
295
 
268
296
  上游 Native Codex Responses 请求没有输出 token 上限字段,因此普通会话若显式设置 `maxTokens` 仍会在 Provider I/O 前失败。DSH 的压缩与会话标题辅助调用会固定携带 `maxTokens`,并分别通过 `purpose: compaction` 与 `purpose: session-title` 标识;本适配器允许这两类 hint 通过,但不会把它序列化为 `max_output_tokens`,实际输出长度仍由 Native Codex 决定。
269
297
 
@@ -6,6 +6,21 @@ import type { NativeCodexModel, NativeCodexModelCatalog } from './catalog.js';
6
6
  export declare const CODEX_PROVIDER = "openai-codex";
7
7
  /** Compatibility route retained for sessions created during the native preview. */
8
8
  export declare const NATIVE_CODEX_PROVIDER = "openai-codex-native";
9
+ declare module '@deepseek-ai/cordis' {
10
+ interface Events {
11
+ /**
12
+ * Native replay ownership changed.
13
+ * @mode emit
14
+ */
15
+ 'codex/replay-owners-updated'(): void;
16
+ }
17
+ }
18
+ /**
19
+ * Snapshot immutable provider groups for a pure projection's replay policy.
20
+ * @param runtime - current LLM service, or undefined before it is available.
21
+ * @returns sorted groups; each group is owned by one native adapter instance.
22
+ */
23
+ export declare function nativeCodexReplayGroups(runtime: LlmRuntime | undefined): readonly (readonly string[])[];
9
24
  /**
10
25
  * Register native routes and their replay-pricing ownership in the same Cordis scope.
11
26
  * @param ctx - scope owning the native adapter registration.
@@ -7,6 +7,23 @@ export const CODEX_PROVIDER = 'openai-codex';
7
7
  export const NATIVE_CODEX_PROVIDER = 'openai-codex-native';
8
8
  const OWNED_CODEX_PROVIDERS = new Set([CODEX_PROVIDER, NATIVE_CODEX_PROVIDER]);
9
9
  const replayOwners = new WeakMap();
10
+ /**
11
+ * Snapshot immutable provider groups for a pure projection's replay policy.
12
+ * @param runtime - current LLM service, or undefined before it is available.
13
+ * @returns sorted groups; each group is owned by one native adapter instance.
14
+ */
15
+ export function nativeCodexReplayGroups(runtime) {
16
+ if (runtime === undefined)
17
+ return Object.freeze([]);
18
+ const groups = new Map();
19
+ for (const [provider, adapter] of replayOwners.get(runtimeKey(runtime)) ?? []) {
20
+ const group = groups.get(adapter) ?? [];
21
+ group.push(provider);
22
+ groups.set(adapter, group);
23
+ }
24
+ return Object.freeze([...groups.values()].map(group => Object.freeze(group.sort()))
25
+ .sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b))));
26
+ }
10
27
  // Cordis returns scope-traced views; use the original service for ownership identity.
11
28
  function runtimeKey(runtime) {
12
29
  return Reflect.get(runtime, symbols.original) ?? runtime;
@@ -28,11 +45,13 @@ export function registerNativeCodexAdapter(ctx, providers, adapter) {
28
45
  replayOwners.set(runtimeKey(runtime), owners = new Map());
29
46
  for (const provider of providers)
30
47
  owners.set(provider, adapter);
48
+ ctx.emit('codex/replay-owners-updated');
31
49
  return () => {
32
50
  for (const provider of providers) {
33
51
  if (owners.get(provider) === adapter)
34
52
  owners.delete(provider);
35
53
  }
54
+ ctx.emit('codex/replay-owners-updated');
36
55
  };
37
56
  });
38
57
  }
@@ -0,0 +1,69 @@
1
+ import { type Context } from '@deepseek-ai/cordis';
2
+ import { z } from 'zod';
3
+ import { SessionSeq } from '@deepseek-ai/dsh-session';
4
+ import type { ProjectionDefinition, SessionProjectionMap, SessionProjectionStateMap } from '@deepseek-ai/dsh-session-projection';
5
+ type BaseState = SessionProjectionStateMap['contextPressure'];
6
+ type View = SessionProjectionMap['contextPressure'];
7
+ type PressureDefinition = ProjectionDefinition<'contextPressure'> & {
8
+ wire: NonNullable<ProjectionDefinition<'contextPressure'>['wire']>;
9
+ };
10
+ declare const extraSchema: z.ZodObject<{
11
+ header: z.ZodNullable<z.ZodObject<{
12
+ provider: z.ZodString;
13
+ model: z.ZodString;
14
+ fingerprint: z.ZodString;
15
+ tools: z.ZodNumber;
16
+ }, z.core.$strict>>;
17
+ sample: z.ZodNullable<z.ZodObject<{
18
+ provider: z.ZodString;
19
+ model: z.ZodString;
20
+ fingerprint: z.ZodString;
21
+ tools: z.ZodNumber;
22
+ }, z.core.$strict>>;
23
+ sampleNative: z.ZodNullable<z.ZodBoolean>;
24
+ nativeHistory: z.ZodBoolean;
25
+ context: z.ZodNullable<z.ZodObject<{
26
+ provider: z.ZodString;
27
+ model: z.ZodString;
28
+ }, z.core.$strict>>;
29
+ nodes: z.ZodArray<z.ZodObject<{
30
+ seq: z.ZodPipe<z.ZodNumber, z.ZodTransform<SessionSeq, number>>;
31
+ position: z.ZodPipe<z.ZodNumber, z.ZodTransform<SessionSeq, number>>;
32
+ provider: z.ZodString;
33
+ tokens: z.ZodNumber;
34
+ }, z.core.$strict>>;
35
+ positions: z.ZodArray<z.ZodObject<{
36
+ seq: z.ZodPipe<z.ZodNumber, z.ZodTransform<SessionSeq, number>>;
37
+ position: z.ZodPipe<z.ZodNumber, z.ZodTransform<SessionSeq, number>>;
38
+ }, z.core.$strict>>;
39
+ sampledHidden: z.ZodRecord<z.ZodString, z.ZodNumber>;
40
+ }, z.core.$strict>;
41
+ type Extra = z.infer<typeof extraSchema> & {
42
+ view: View;
43
+ };
44
+ type State = BaseState & {
45
+ codex: Extra;
46
+ };
47
+ /**
48
+ * Withhold only the host pressure registration in an isolated service view.
49
+ * All other registrations use the original caller-traced registry and disposer.
50
+ * @param ctx - scope owning the replacement meter.
51
+ * @returns scoped constructor context and the captured host pressure definition.
52
+ */
53
+ export declare function capturePressureRegistration(ctx: Context): {
54
+ context: Context;
55
+ readonly definition: PressureDefinition;
56
+ register<S extends BaseState>(definition: ProjectionDefinition<'contextPressure', S>): () => void;
57
+ };
58
+ /**
59
+ * Add hidden replay to the existing UI pressure fields using only logged data.
60
+ * Native groups are immutable policy; source prices and sample totals stay
61
+ * policy-independent so checkpoints can be safely restored after ownership changes.
62
+ * @param base - host pressure fold, schema, and wire view.
63
+ * @param groups - provider routes grouped by their active native adapter.
64
+ * @returns one pure pressure unit with a policy-independent checkpoint format.
65
+ */
66
+ export declare function codexPressureProjection(base: PressureDefinition, groups: readonly (readonly string[])[]): ProjectionDefinition<'contextPressure', State> & {
67
+ wire: NonNullable<ProjectionDefinition<'contextPressure', State>['wire']>;
68
+ };
69
+ export {};
@@ -0,0 +1,216 @@
1
+ /** Pure, route-aware UI pressure pricing and scoped host registration forwarding. */
2
+ import { createHash } from 'node:crypto';
3
+ import { symbols } from '@deepseek-ai/cordis';
4
+ import { z } from 'zod';
5
+ import { lastAssistantStreamChunk } from '@deepseek-ai/dsh-llm';
6
+ import { canonicalHeader, deriveEventMessage, isSurfaceEvent, SessionSeq, } from '@deepseek-ai/dsh-session';
7
+ import { estimateToolsTokens } from '@deepseek-ai/dsh-token-meter/estimate';
8
+ import { CODEX_PROVIDER, NATIVE_CODEX_PROVIDER } from './native-adapter.js';
9
+ import { estimateNativeCodexReplayTokens, hasNativeCodexReplayKind } from './replay.js';
10
+ const sequence = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).transform(SessionSeq);
11
+ const count = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
12
+ const routeSchema = z.object({ provider: z.string(), model: z.string(), fingerprint: z.string(), tools: count }).strict();
13
+ const extraSchema = z.object({
14
+ header: routeSchema.nullable(),
15
+ sample: routeSchema.nullable(),
16
+ sampleNative: z.boolean().nullable(),
17
+ nativeHistory: z.boolean(),
18
+ context: z.object({ provider: z.string(), model: z.string() }).strict().nullable(),
19
+ nodes: z.array(z.object({ seq: sequence, position: sequence, provider: z.string(), tokens: count }).strict()),
20
+ positions: z.array(z.object({ seq: sequence, position: sequence }).strict()),
21
+ sampledHidden: z.record(z.string(), count),
22
+ }).strict();
23
+ function route(header) {
24
+ const normalized = canonicalHeader(header);
25
+ return {
26
+ provider: normalized.config.provider,
27
+ model: normalized.config.model,
28
+ fingerprint: createHash('sha256').update(JSON.stringify({
29
+ config: {
30
+ provider: normalized.config.provider, model: normalized.config.model,
31
+ reasoningEffort: normalized.config.reasoningEffort, temperature: normalized.config.temperature,
32
+ maxTokens: normalized.config.maxTokens, stop: normalized.config.stop,
33
+ },
34
+ adapterDefaults: {
35
+ reasoningEffort: normalized.adapterDefaults?.reasoningEffort,
36
+ maxTokens: normalized.adapterDefaults?.maxTokens,
37
+ },
38
+ tools: normalized.tools ?? [],
39
+ })).digest('hex'),
40
+ tools: estimateToolsTokens(normalized),
41
+ };
42
+ }
43
+ function sums(nodes) {
44
+ const totals = {};
45
+ for (const node of nodes)
46
+ totals[node.provider] = (totals[node.provider] ?? 0) + node.tokens;
47
+ return totals;
48
+ }
49
+ function sameView(a, b) {
50
+ return a.pressureTokens === b.pressureTokens && a.projectedTokens === b.projectedTokens
51
+ && a.contextWindow === b.contextWindow;
52
+ }
53
+ /**
54
+ * Withhold only the host pressure registration in an isolated service view.
55
+ * All other registrations use the original caller-traced registry and disposer.
56
+ * @param ctx - scope owning the replacement meter.
57
+ * @returns scoped constructor context and the captured host pressure definition.
58
+ */
59
+ export function capturePressureRegistration(ctx) {
60
+ const registry = ctx.sessionProjections;
61
+ const originalRegister = registry.register;
62
+ let captured;
63
+ const register = (definition) => {
64
+ if (definition.key === 'contextPressure') {
65
+ if (definition.wire === undefined)
66
+ throw new Error('host pressure projection has no wire view');
67
+ captured = { ...definition, wire: definition.wire };
68
+ return () => { };
69
+ }
70
+ return Reflect.apply(originalRegister, registry, [definition]);
71
+ };
72
+ const forwarded = new Proxy(registry, {
73
+ get(target, key, receiver) {
74
+ // Suppress another Cordis trace wrapper, which would bypass the register trap.
75
+ if (key === symbols.tracker)
76
+ return undefined;
77
+ if (key === 'register')
78
+ return register;
79
+ return Reflect.get(target, key, receiver);
80
+ },
81
+ });
82
+ const context = ctx.isolate('sessionProjections');
83
+ context.provide('sessionProjections', forwarded);
84
+ return { context, register(definition) {
85
+ return Reflect.apply(originalRegister, registry, [definition]);
86
+ }, get definition() {
87
+ if (captured === undefined)
88
+ throw new Error('host token meter did not register contextPressure');
89
+ return captured;
90
+ } };
91
+ }
92
+ /**
93
+ * Add hidden replay to the existing UI pressure fields using only logged data.
94
+ * Native groups are immutable policy; source prices and sample totals stay
95
+ * policy-independent so checkpoints can be safely restored after ownership changes.
96
+ * @param base - host pressure fold, schema, and wire view.
97
+ * @param groups - provider routes grouped by their active native adapter.
98
+ * @returns one pure pressure unit with a policy-independent checkpoint format.
99
+ */
100
+ export function codexPressureProjection(base, groups) {
101
+ const owners = new Map(groups.flatMap((group, index) => group.map(provider => [provider, index])));
102
+ const hidden = (totals, target) => {
103
+ const owner = owners.get(target);
104
+ if (owner === undefined)
105
+ return 0;
106
+ return Object.entries(totals).reduce((n, [provider, tokens]) => n + (owners.get(provider) === owner ? tokens : 0), 0);
107
+ };
108
+ const view = (state, extra) => {
109
+ const original = base.wire.view(state);
110
+ const header = extra.header;
111
+ if (header === null)
112
+ return original;
113
+ const native = owners.has(header.provider);
114
+ const sampleProvider = extra.sample?.provider;
115
+ const changedEnvelope = extra.sample !== null && extra.sample.fingerprint !== header.fingerprint;
116
+ if (!native && !extra.nativeHistory && extra.sampleNative !== true
117
+ && !owners.has(sampleProvider ?? '') && !changedEnvelope)
118
+ return original;
119
+ const current = hidden(sums(extra.nodes), header.provider);
120
+ const sampled = hidden(extra.sampledHidden, header.provider);
121
+ const sampledSources = Object.entries(extra.sampledHidden).filter(([, tokens]) => tokens > 0);
122
+ const policyCompatible = native
123
+ ? sampledSources.every(([source]) => source === extra.sample?.provider
124
+ && owners.get(source) === owners.get(header.provider))
125
+ : extra.sampleNative === false || sampledSources.length === 0 && extra.sampleNative !== true;
126
+ const compatible = extra.sample?.fingerprint === header.fingerprint
127
+ && (extra.sampleNative === null || extra.sampleNative === native) && policyCompatible
128
+ && state.pressureTokens !== undefined
129
+ && state.sampledSurfaceTokens !== undefined
130
+ && state.pressureTokens >= state.sampledSurfaceTokens + header.tools + sampled;
131
+ const projectedTokens = compatible
132
+ ? Math.max(0, state.pressureTokens + state.surfaceTokens - state.sampledSurfaceTokens + current - sampled)
133
+ : state.surfaceTokens + header.tools + current;
134
+ const { contextWindow: _oldWindow, ...reported } = original;
135
+ return {
136
+ ...reported, projectedTokens,
137
+ ...extra.context?.provider === header.provider && extra.context.model === header.model
138
+ && original.contextWindow !== undefined ? { contextWindow: original.contextWindow } : {},
139
+ };
140
+ };
141
+ const stateSchema = z.record(z.string(), z.unknown()).transform((value, ctx) => {
142
+ const { codex, ...legacy } = value;
143
+ const parsedBase = base.stateSchema.safeParse(legacy);
144
+ const parsedExtra = extraSchema.safeParse(typeof codex === 'object' && codex !== null
145
+ ? Object.fromEntries(Object.entries(codex).filter(([key]) => key !== 'view')) : codex);
146
+ if (!parsedBase.success || !parsedExtra.success) {
147
+ ctx.addIssue({ code: 'custom', message: 'invalid Codex pressure checkpoint' });
148
+ return z.NEVER;
149
+ }
150
+ return { ...parsedBase.data, codex: { ...parsedExtra.data, view: view(parsedBase.data, parsedExtra.data) } };
151
+ });
152
+ return {
153
+ key: 'contextPressure', stateVersion: 6, stateSchema,
154
+ init: (header, inherited) => {
155
+ const initial = base.init(header, inherited);
156
+ const extra = {
157
+ header: null, sample: null, sampleNative: null, nativeHistory: false, context: null,
158
+ nodes: [], positions: [], sampledHidden: {},
159
+ };
160
+ return { ...initial, codex: { ...extra, view: view(initial, extra) } };
161
+ },
162
+ apply: (state, event) => {
163
+ const next = base.apply(state, event);
164
+ let extra = state.codex;
165
+ if (event.type === 'request/header') {
166
+ const nextRoute = route(event.data.header);
167
+ if (nextRoute.fingerprint !== extra.header?.fingerprint)
168
+ extra = { ...extra, header: nextRoute };
169
+ }
170
+ if (event.type === 'request/context')
171
+ extra = { ...extra, context: { provider: event.data.provider, model: event.data.model } };
172
+ if (event.type === 'assistant/message' || event.type === 'assistant/attempt') {
173
+ const usage = event.type === 'assistant/message' && event.data.usage !== undefined
174
+ ? event.data.usage : lastAssistantStreamChunk(event.data.stream, 'usage')?.usage;
175
+ if (usage !== undefined) {
176
+ const finish = lastAssistantStreamChunk(event.data.stream, 'finish');
177
+ const replay = finish?.replayState ?? (event.type === 'assistant/message'
178
+ && event.data.message.source.kind === 'model' ? event.data.message.source.replayState : undefined);
179
+ const native = replay === undefined || replay === null ? null : hasNativeCodexReplayKind(replay);
180
+ extra = { ...extra, sample: extra.header, sampledHidden: sums(extra.nodes), sampleNative: native,
181
+ nativeHistory: extra.nativeHistory || native === true };
182
+ }
183
+ }
184
+ if (isSurfaceEvent(event)) {
185
+ const message = deriveEventMessage(event);
186
+ const source = message?.role === 'assistant' && message.source.kind === 'model' ? message.source : undefined;
187
+ const originalNative = source !== undefined && hasNativeCodexReplayKind(source.replayState)
188
+ && (source.provider === CODEX_PROVIDER || source.provider === NATIVE_CODEX_PROVIDER);
189
+ const price = originalNative ? estimateNativeCodexReplayTokens(source, event.type === 'assistant/message' ? event.data.usage : undefined) : 0;
190
+ let nodes = extra.nodes;
191
+ let positions = extra.positions;
192
+ let position = event.seq;
193
+ if (event.surfaceOp !== 'append') {
194
+ const { startSeq, endSeq } = event.surfaceOp;
195
+ const first = positions.find(node => node.seq === startSeq)?.position ?? startSeq;
196
+ const last = positions.find(node => node.seq === endSeq)?.position ?? endSeq;
197
+ if (last < first)
198
+ throw new Error('Codex pressure: invalid positional replacement');
199
+ position = first;
200
+ nodes = nodes.filter(node => node.position < first || node.position > last);
201
+ positions = [...positions.filter(node => node.position < first || node.position > last), { seq: event.seq, position }];
202
+ }
203
+ if (price > 0 && source !== undefined)
204
+ nodes = [...nodes, { seq: event.seq, position, provider: source.provider, tokens: price }];
205
+ if (nodes !== extra.nodes || positions !== extra.positions || originalNative && !extra.nativeHistory) {
206
+ extra = { ...extra, nodes, positions, nativeHistory: extra.nativeHistory || originalNative };
207
+ }
208
+ }
209
+ if (next === state && extra === state.codex)
210
+ return state;
211
+ const output = view(next, extra);
212
+ return { ...next, codex: { ...extra, view: sameView(output, state.codex.view) ? state.codex.view : output } };
213
+ },
214
+ wire: { viewSchema: base.wire.viewSchema, view: state => state.codex.view },
215
+ };
216
+ }
@@ -1,15 +1,19 @@
1
1
  /** Codex hidden-replay pricing layered over the host's ordinary token meter. */
2
2
  import { TokenMeter } from '@deepseek-ai/dsh-token-meter';
3
- import type { TokenMeasurement } from '@deepseek-ai/dsh-token-meter';
3
+ import type { TokenMeasurement, TokenMeterConfig } from '@deepseek-ai/dsh-token-meter';
4
+ import type { Context } from '@deepseek-ai/cordis';
4
5
  import type { EpochHeader, Session } from '@deepseek-ai/dsh-session';
5
6
  /**
6
7
  * Replacement tokenMeter service that charges native Codex encrypted reasoning.
7
- * Ordinary prices and projections come from the host meter. Hidden prices apply
8
+ * Ordinary prices, usage, and breakdown projections come from the host meter.
9
+ * The UI pressure unit adds the same hidden pricing per logged request route. Hidden prices apply
8
10
  * only between routes currently owned by the same native adapter; fixed
9
11
  * heuristicTokens stay unchanged. Provider usage is not charged a second time.
10
12
  */
11
13
  export declare class CodexTokenMeter extends TokenMeter {
14
+ static inject: string[];
12
15
  private readonly hiddenFolds;
16
+ constructor(ctx: Context, config?: TokenMeterConfig);
13
17
  /**
14
18
  * Return a detached measurement including opaque native replay in node prices,
15
19
  * surface totals, and signed anchor deltas. Recheck usage conservatism against
@@ -1,9 +1,10 @@
1
1
  /** Codex hidden-replay pricing layered over the host's ordinary token meter. */
2
2
  import { TokenMeter } from '@deepseek-ai/dsh-token-meter';
3
+ import { capturePressureRegistration, codexPressureProjection } from './pressure-projection.js';
3
4
  import { estimateToolsTokens } from '@deepseek-ai/dsh-token-meter/estimate';
4
5
  import { assembleAssistantStream } from '@deepseek-ai/dsh-llm';
5
6
  import { canonicalHeader, deriveEventMessage, headerEquals, isSurfaceEvent, SessionSeq, } from '@deepseek-ai/dsh-session';
6
- import { canReplayNativeCodex } from './native-adapter.js';
7
+ import { canReplayNativeCodex, nativeCodexReplayGroups } from './native-adapter.js';
7
8
  import { estimateNativeCodexReplayTokens } from './replay.js';
8
9
  function replayPrice(message, usage) {
9
10
  if (message?.role !== 'assistant' || message.source.kind !== 'model')
@@ -15,12 +16,32 @@ function sameHeader(a, b) {
15
16
  }
16
17
  /**
17
18
  * Replacement tokenMeter service that charges native Codex encrypted reasoning.
18
- * Ordinary prices and projections come from the host meter. Hidden prices apply
19
+ * Ordinary prices, usage, and breakdown projections come from the host meter.
20
+ * The UI pressure unit adds the same hidden pricing per logged request route. Hidden prices apply
19
21
  * only between routes currently owned by the same native adapter; fixed
20
22
  * heuristicTokens stay unchanged. Provider usage is not charged a second time.
21
23
  */
22
24
  export class CodexTokenMeter extends TokenMeter {
25
+ static inject = ['sessionProjections', 'llm'];
23
26
  hiddenFolds = new WeakMap();
27
+ constructor(ctx, config) {
28
+ const capture = capturePressureRegistration(ctx);
29
+ super(capture.context, config);
30
+ let unregister;
31
+ let policy;
32
+ const refresh = () => {
33
+ const groups = nativeCodexReplayGroups(ctx.get('llm'));
34
+ const fingerprint = JSON.stringify(groups);
35
+ if (fingerprint === policy)
36
+ return;
37
+ unregister?.();
38
+ unregister = capture.register(codexPressureProjection(capture.definition, groups));
39
+ policy = fingerprint;
40
+ };
41
+ refresh();
42
+ ctx.on('codex/replay-owners-updated', refresh);
43
+ ctx.effect(() => () => { unregister?.(); });
44
+ }
24
45
  /**
25
46
  * Return a detached measurement including opaque native replay in node prices,
26
47
  * surface totals, and signed anchor deltas. Recheck usage conservatism against
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pure01fx/dsh-openai-codex-auth",
3
- "version": "0.13.3",
3
+ "version": "0.13.4",
4
4
  "description": "Native ChatGPT Codex provider, device-code-first login, and same-origin Web integration for DeepSeek Harness",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -61,6 +61,8 @@
61
61
  "lib/replay.d.ts",
62
62
  "lib/token-meter.js",
63
63
  "lib/token-meter.d.ts",
64
+ "lib/pressure-projection.js",
65
+ "lib/pressure-projection.d.ts",
64
66
  "lib/retry-after.js",
65
67
  "lib/retry-after.d.ts",
66
68
  "lib/rate-limits.js",
@@ -123,6 +125,7 @@
123
125
  },
124
126
  "peerDependencies": {
125
127
  "@deepseek-ai/dsh-session": "0.2.0-rc.2",
128
+ "@deepseek-ai/dsh-session-projection": "0.2.0-rc.2",
126
129
  "@deepseek-ai/cordis": "~4.0.4",
127
130
  "@deepseek-ai/dsh-attachment": "0.2.0-rc.2",
128
131
  "@deepseek-ai/dsh-credentials": "0.2.0-rc.2",
@@ -142,7 +145,8 @@
142
145
  "https-proxy-agent": "7.0.6",
143
146
  "image-size": "^2.0.2",
144
147
  "proxy-from-env": "1.1.0",
145
- "ws": "8.21.3"
148
+ "ws": "8.21.3",
149
+ "zod": "^4.4.3"
146
150
  },
147
151
  "devDependencies": {
148
152
  "@deepseek-ai/cordis": "~4.0.4",