@pure01fx/dsh-openai-codex-auth 0.11.2 → 0.12.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/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.12.0
6
+
7
+ ### Migration
8
+
9
+ - Migrated to DSH 0.1.7-rc.2 SDK
10
+ - Updated tool message format: tool results are now role:'tool' with top-level toolCallId/isError
11
+ - Migrated shell.run to shell.execute with result() method
12
+ - Updated peer dependencies to 0.1.7-rc.2
13
+ - Updated cordis to ~4.0.4
14
+ - Preserved developer messages in HTTP/WS wire requests
15
+ - Restored successful-result-only matched provenance in cloud context
16
+ - Added mismatch/failed-result regressions in cloud context tests
17
+
18
+ ### Key Changes
19
+
20
+ - Tool messages: role:'tool' with top-level toolCallId/isError (no longer tool-result content blocks)
21
+ - Shell: execute() then .result() instead of run()
22
+ - Developer messages: preserved in Responses API requests
23
+ - Cloud context: requires toolCallId === source.callId and !isError
24
+
5
25
  ## 0.11.2
6
26
 
7
27
  - Requests the live model catalog as Codex 0.155.0 so version-gated GPT-6 Sol can be advertised; preserves dynamic account-specific discovery and routes exact Sol requests through Responses Lite when catalog metadata is unavailable. Fast remains catalog-authorized.
@@ -131,4 +151,4 @@
131
151
  - Rejects redirects for credential-bearing OAuth, Catalog, Responses, and subscription-usage requests.
132
152
  - Keeps OAuth credentials and account authority host-side, validates credential-bearing endpoint authorities, bounds request/response/replay/WebSocket state, and refuses managed/external credential mismatches.
133
153
  - Pins Host/runtime contracts to DSH rc.6 and includes synchronized JavaScript/declaration artifacts plus the complete native transport modules.
134
- - Keeps the package name `@pure01fx/dsh-openai-codex-auth`; a rename is intentionally deferred.
154
+ - Keeps the package name `@pure01fx/dsh-openai-codex-auth`; a rename is intentionally deferred.
package/README.md CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  ## 快速开始
19
19
 
20
- 版本 `0.11.1` 明确适配 DeepSeek Harness `0.1.5-rc.1`,SDK peers/runtime helpers 精确锁定该版本;不声明兼容旧 SDK。旧 DSH `0.1.1-rc.2` profile 应保留原插件 `0.10.0`,升级时在独立 profile 安装新版插件并保留原 profile/锁文件以便回滚。无需迁移账号文件或浏览器隐藏列表;`dsh.engines` 仅作兼容性说明,不当作宿主硬门禁。
20
+ 版本 `0.12.0` 明确适配 DeepSeek Harness `0.1.7-rc.2`,SDK peers/runtime helpers 精确锁定该版本;不声明兼容旧 SDK。旧 DSH `0.1.5-rc.1` profile 应保留原插件 `0.11.2`,升级时在独立 profile 安装新版插件并保留原 profile/锁文件以便回滚。无需迁移账号文件或浏览器隐藏列表;`dsh.engines` 仅作兼容性说明,不当作宿主硬门禁。
21
21
 
22
22
  本版本通过真实目标 LLM/tools 服务与本地 HTTP/SSE/WebSocket 回放、错误/取消回归;模型隐藏覆盖真实目录/catalog/store 的投影更新、重连和重新加载。验证命令为 `pnpm typecheck && pnpm build && pnpm test`;当前版本未执行线上 OAuth/付费模型验收。
23
23
 
@@ -356,4 +356,4 @@ node --check client.js
356
356
 
357
357
  ## License
358
358
 
359
- [MIT](./LICENSE)
359
+ [MIT](./LICENSE)
@@ -14,16 +14,6 @@ export function chooseCloudModel(params, models) {
14
14
  export function visibleCloudImages(messages) {
15
15
  const images = [];
16
16
  const calls = new Set();
17
- const visit = (blocks, depth = 0) => {
18
- if (depth > 16)
19
- return;
20
- for (const block of blocks) {
21
- if (block.type === 'image')
22
- images.push(block.attachment);
23
- else if (block.type === 'tool-result' && !block.isError && calls.has(block.toolCallId))
24
- visit(block.content, depth + 1);
25
- }
26
- };
27
17
  for (const message of messages) {
28
18
  if (message.role === 'assistant') {
29
19
  for (const block of message.content)
@@ -36,10 +26,16 @@ export function visibleCloudImages(messages) {
36
26
  if (block.type === 'image')
37
27
  images.push(block.attachment);
38
28
  }
39
- else if (message.role === 'user' && message.source.kind === 'tool' && calls.has(message.source.callId)) {
29
+ else if (message.role === 'tool' && message.source.kind === 'tool') {
30
+ // Require successful result with matching callId and earlier matching call
31
+ if (message.isError || message.toolCallId !== message.source.callId)
32
+ continue;
33
+ if (!calls.has(message.source.callId))
34
+ continue;
35
+ // Tool messages with images in their content
40
36
  for (const block of message.content)
41
- if (block.type === 'tool-result' && block.toolCallId === message.source.callId && !block.isError)
42
- visit(block.content);
37
+ if (block.type === 'image')
38
+ images.push(block.attachment);
43
39
  }
44
40
  }
45
41
  return images;
@@ -24,7 +24,7 @@ export interface ResolvedCloudImage {
24
24
  export declare function resolveImageSources(value: ImageSelection, deps: MediaSourceDependencies): Promise<ResolvedCloudImage[]>;
25
25
  export interface MediaOutputDependencies {
26
26
  fs: Pick<FileSystem, 'resolve' | 'processPath' | 'contains'>;
27
- shell: Pick<ShellExecutor, 'resolve' | 'run' | 'sandboxMode'>;
27
+ shell: Pick<ShellExecutor, 'resolve' | 'execute' | 'sandboxMode'>;
28
28
  /** Resolve from the calling session with the public sandboxPolicy service. */
29
29
  policy: SandboxExecutionPolicy;
30
30
  attachments: Pick<AttachmentStore, 'validateImage' | 'saveImage'>;
@@ -92,11 +92,12 @@ export async function saveImageOutputs(response, deps) {
92
92
  if (!deps.fs.contains(workspace, target))
93
93
  throw new Error('Original image target escapes caller workspace');
94
94
  const path = deps.fs.processPath(target);
95
- const outcome = await deps.shell.run(deps.shell.resolve({
95
+ const execution = await deps.shell.execute(deps.shell.resolve({
96
96
  command: 'node -e ' + quote(WRITE_ORIGINAL) + ' -- ' + quote(path),
97
97
  stdin: Buffer.from(image.data).toString('base64'), workdir: deps.fs.processPath(workspace),
98
98
  sandboxPolicy: deps.policy, signal: deps.signal, timeoutMs: 30_000, stdoutMaxBytes: 1024,
99
99
  }));
100
+ const outcome = await execution.result();
100
101
  if (outcome.sandbox?.denied || outcome.sandbox?.runnerFailed || outcome.exitCode !== 0 || outcome.aborted || outcome.timedOut)
101
102
  throw new Error('Original image write failed under caller filesystem policy');
102
103
  const attachment = await deps.attachments.saveImage({ data: image.data, mediaType: image.mediaType, name });
@@ -124,12 +124,11 @@ function checkReferences(args, messages, id, recent) {
124
124
  for (const block of message.content)
125
125
  if (block.type === 'tool-call')
126
126
  calls.set(block.id, block.name);
127
- if (message.source.kind !== 'tool' || !['codex_web', 'run_code'].includes(calls.get(message.source.callId) ?? ''))
127
+ if (message.role !== 'tool' || message.isError || message.source.kind !== 'tool'
128
+ || message.toolCallId !== message.source.callId
129
+ || !['codex_web', 'run_code'].includes(calls.get(message.toolCallId) ?? ''))
128
130
  continue;
129
- for (const block of message.content)
130
- if (block.type === 'tool-result' && block.toolCallId === message.source.callId && !block.isError) {
131
- outputs.push(block.content.filter(part => part.type === 'text').map(part => part.text).join('\n'));
132
- }
131
+ outputs.push(message.content.filter(block => block.type === 'text').map(block => block.text).join('\n'));
133
132
  }
134
133
  outputs.push(...recent);
135
134
  for (const ref of refs) {
@@ -122,28 +122,29 @@ async function resolveImage(block, options, signal) {
122
122
  ...stored.detail === undefined ? {} : { detail: stored.detail },
123
123
  };
124
124
  }
125
- async function resolveToolResult(block, options, signal) {
126
- const content = [];
127
- for (const part of block.content) {
128
- throwIfAborted(signal);
129
- if (part.type === 'text')
130
- content.push(part);
131
- else if (part.type === 'image')
132
- content.push(await resolveImage(part, options, signal));
133
- else {
134
- throw fixedFailure('native Codex tool output contains an unsupported content block', 'UNSUPPORTED');
135
- }
136
- }
137
- return {
138
- type: 'tool-result',
139
- toolCallId: block.toolCallId,
140
- content,
141
- ...block.isError === undefined ? {} : { isError: block.isError },
142
- };
143
- }
144
125
  async function resolveMessages(generation, options) {
145
126
  const messages = [];
146
127
  for (const message of generation.messages) {
128
+ if (message.role === 'tool') {
129
+ const content = [];
130
+ for (const block of message.content) {
131
+ throwIfAborted(generation.signal);
132
+ if (block.type === 'text')
133
+ content.push(block);
134
+ else if (block.type === 'image')
135
+ content.push(await resolveImage(block, options, generation.signal));
136
+ else {
137
+ throw fixedFailure('native Codex tool output contains an unsupported content block', 'UNSUPPORTED');
138
+ }
139
+ }
140
+ messages.push({
141
+ role: 'tool',
142
+ content,
143
+ toolCallId: message.toolCallId,
144
+ ...message.isError !== undefined ? { isError: message.isError } : {},
145
+ });
146
+ continue;
147
+ }
147
148
  const content = [];
148
149
  const source = message.source;
149
150
  const sourceKind = source?.kind;
@@ -177,12 +178,6 @@ async function resolveMessages(generation, options) {
177
178
  // A settlement copies the child's assistant output into user-role context.
178
179
  // Its tool calls belong to the child and must not become parent wire calls.
179
180
  break;
180
- case 'tool-result':
181
- if (message.role !== 'user') {
182
- throw fixedFailure('native Codex tool results require user messages', 'INVALID_ARGS');
183
- }
184
- content.push(await resolveToolResult(block, options, generation.signal));
185
- break;
186
181
  default:
187
182
  throw fixedFailure('native Codex request contains an unsupported content block', 'UNSUPPORTED');
188
183
  }
@@ -191,6 +186,7 @@ async function resolveMessages(generation, options) {
191
186
  && source.replayState !== undefined && hasNativeCodexReplayKind(source.replayState)
192
187
  ? { provider: source.provider, model: source.model, replayState: source.replayState }
193
188
  : undefined;
189
+ // Developer messages are preserved for the Responses API
194
190
  messages.push({
195
191
  role: message.role,
196
192
  content,
@@ -98,7 +98,8 @@ function sessionKey(generation, routingId) {
98
98
  function turnKey(generation) {
99
99
  for (let index = generation.messages.length - 1; index >= 0; index--) {
100
100
  const message = generation.messages[index];
101
- if (message?.source.kind === 'user') {
101
+ // RequestUserInput doesn't have source or id
102
+ if (message && 'source' in message && message.source?.kind === 'user' && 'id' in message) {
102
103
  return createHash('sha256').update(String(message.id)).digest('base64url');
103
104
  }
104
105
  }
@@ -9,18 +9,14 @@ export interface ResolvedImagePart {
9
9
  dataBase64: string;
10
10
  detail?: CodexImageDetail;
11
11
  }
12
- export interface ResolvedToolResultPart {
13
- type: 'tool-result';
14
- toolCallId: ToolCallId;
15
- content: readonly ResolvedContentPart[];
16
- isError?: boolean;
17
- }
18
12
  export type ResolvedContentPart = Exclude<ContentBlock, {
19
- type: 'image' | 'tool-result';
20
- }> | ResolvedImagePart | ResolvedToolResultPart;
13
+ type: 'image';
14
+ }> | ResolvedImagePart;
21
15
  export interface ResolvedMessage {
22
- role: 'system' | 'user' | 'assistant';
16
+ role: 'system' | 'user' | 'assistant' | 'tool' | 'developer';
23
17
  content: readonly ResolvedContentPart[];
18
+ toolCallId?: ToolCallId;
19
+ isError?: boolean;
24
20
  replaySource?: NativeCodexReplaySource;
25
21
  }
26
22
  export interface ResponsesRequestInput {
package/lib/responses.js CHANGED
@@ -26,12 +26,12 @@ function imageItem(image) {
26
26
  return { type: 'input_image', image_url: `data:${image.mediaType};base64,${image.dataBase64}`,
27
27
  ...image.detail === undefined ? {} : { detail: image.detail } };
28
28
  }
29
- function toolOutput(block) {
30
- const images = block.content.some(part => part.type === 'image');
29
+ function toolOutput(content) {
30
+ const images = content.some(part => part.type === 'image');
31
31
  if (!images) {
32
- return block.content.map(part => part.type === 'text' ? part.text : '').join('');
32
+ return content.map(part => part.type === 'text' ? part.text : '').join('');
33
33
  }
34
- return block.content.map((part) => {
34
+ return content.map((part) => {
35
35
  if (part.type === 'text')
36
36
  return { type: 'input_text', text: part.text };
37
37
  if (part.type === 'image' && 'dataBase64' in part)
@@ -67,6 +67,29 @@ export function toResponsesInput(messages, system) {
67
67
  input.push({ type: 'message', role: message.role, content });
68
68
  content = [];
69
69
  };
70
+ // Handle tool messages separately - they have no content blocks, just top-level fields
71
+ if (message.role === 'tool') {
72
+ if (message.toolCallId === undefined) {
73
+ throw fixedError('native Codex tool message requires toolCallId', 'INVALID_ARGS');
74
+ }
75
+ input.push({
76
+ type: 'function_call_output',
77
+ call_id: String(message.toolCallId),
78
+ output: toolOutput(message.content),
79
+ ...message.isError === true ? { is_error: true } : {},
80
+ });
81
+ continue;
82
+ }
83
+ // Handle developer messages - map to developer role in Responses API
84
+ if (message.role === 'developer') {
85
+ for (const block of message.content) {
86
+ if (block.type === 'text') {
87
+ content.push({ type: 'input_text', text: block.text });
88
+ }
89
+ }
90
+ flush();
91
+ continue;
92
+ }
70
93
  for (const block of message.content) {
71
94
  if (message.role === 'system') {
72
95
  if (block.type === 'text')
@@ -89,18 +112,15 @@ export function toResponsesInput(messages, system) {
89
112
  content.push(imageItem(block));
90
113
  break;
91
114
  case 'tool-call':
115
+ if (message.role !== 'assistant') {
116
+ throw fixedError('native Codex tool calls require assistant messages', 'INVALID_ARGS');
117
+ }
92
118
  flush();
93
119
  input.push({
94
120
  type: 'function_call', call_id: String(block.id),
95
121
  name: block.name, arguments: block.arguments,
96
122
  });
97
123
  break;
98
- case 'tool-result':
99
- flush();
100
- input.push({
101
- type: 'function_call_output', call_id: String(block.toolCallId), output: toolOutput(block),
102
- });
103
- break;
104
124
  case 'reasoning':
105
125
  break; // Visible reasoning is never replayed; M4 may add encrypted items.
106
126
  default:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pure01fx/dsh-openai-codex-auth",
3
- "version": "0.11.2",
3
+ "version": "0.12.0",
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": {
@@ -88,9 +88,15 @@
88
88
  "LICENSE",
89
89
  "CODEX-COMPATIBILITY.md"
90
90
  ],
91
+ "scripts": {
92
+ "build": "tsc",
93
+ "typecheck": "tsc --noEmit",
94
+ "test": "vitest run",
95
+ "prepack": "pnpm build"
96
+ },
91
97
  "dsh": {
92
98
  "engines": {
93
- "dsh": "0.1.5-rc.1"
99
+ "dsh": "0.1.7-rc.2"
94
100
  },
95
101
  "bundle": {
96
102
  "patch": "./cordis.patch.yml"
@@ -106,20 +112,20 @@
106
112
  }
107
113
  },
108
114
  "peerDependencies": {
109
- "@deepseek-ai/cordis": "^4.0.1",
110
- "@deepseek-ai/dsh-attachment": "0.1.5-rc.1",
111
- "@deepseek-ai/dsh-credentials": "0.1.5-rc.1",
112
- "@deepseek-ai/dsh-fs": "0.1.5-rc.1",
113
- "@deepseek-ai/dsh-host-webserver": "0.1.5-rc.1",
114
- "@deepseek-ai/dsh-llm": "0.1.5-rc.1",
115
- "@deepseek-ai/dsh-sandbox": "0.1.5-rc.1",
116
- "@deepseek-ai/dsh-sandbox-policy": "0.1.5-rc.1",
117
- "@deepseek-ai/dsh-shell": "0.1.5-rc.1",
118
- "@deepseek-ai/dsh-tools": "0.1.5-rc.1"
115
+ "@deepseek-ai/cordis": "~4.0.4",
116
+ "@deepseek-ai/dsh-attachment": "0.1.7-rc.2",
117
+ "@deepseek-ai/dsh-credentials": "0.1.7-rc.2",
118
+ "@deepseek-ai/dsh-fs": "0.1.7-rc.2",
119
+ "@deepseek-ai/dsh-host-webserver": "0.1.7-rc.2",
120
+ "@deepseek-ai/dsh-llm": "0.1.7-rc.2",
121
+ "@deepseek-ai/dsh-sandbox": "0.1.7-rc.2",
122
+ "@deepseek-ai/dsh-sandbox-policy": "0.1.7-rc.2",
123
+ "@deepseek-ai/dsh-shell": "0.1.7-rc.2",
124
+ "@deepseek-ai/dsh-tools": "0.1.7-rc.2"
119
125
  },
120
126
  "dependencies": {
121
- "@deepseek-ai/dsh-atomic-write": "0.1.5-rc.1",
122
- "@deepseek-ai/dsh-home-paths": "0.1.5-rc.1",
127
+ "@deepseek-ai/dsh-atomic-write": "0.1.7-rc.2",
128
+ "@deepseek-ai/dsh-home-paths": "0.1.7-rc.2",
123
129
  "@deepseek-ai/schemastery": "^3.18.1",
124
130
  "https-proxy-agent": "7.0.6",
125
131
  "image-size": "^2.0.2",
@@ -127,29 +133,32 @@
127
133
  "ws": "8.21.3"
128
134
  },
129
135
  "devDependencies": {
130
- "@deepseek-ai/dsh-client-ui-model-selection": "0.1.5-rc.1",
131
- "@deepseek-ai/dsh-web-frontend": "0.1.5-rc.1",
132
- "@deepseek-ai/cordis": "^4.0.1",
133
- "@deepseek-ai/dsh-agent": "0.1.5-rc.1",
134
- "@deepseek-ai/dsh-attachment": "0.1.5-rc.1",
135
- "@deepseek-ai/dsh-attachment-local": "0.1.5-rc.1",
136
- "@deepseek-ai/dsh-credentials": "0.1.5-rc.1",
137
- "@deepseek-ai/dsh-fs": "0.1.5-rc.1",
138
- "@deepseek-ai/dsh-host-webserver": "0.1.5-rc.1",
139
- "@deepseek-ai/dsh-llm": "0.1.5-rc.1",
140
- "@deepseek-ai/dsh-sandbox": "0.1.5-rc.1",
141
- "@deepseek-ai/dsh-sandbox-policy": "0.1.5-rc.1",
142
- "@deepseek-ai/dsh-scope": "0.1.5-rc.1",
143
- "@deepseek-ai/dsh-session": "0.1.5-rc.1",
144
- "@deepseek-ai/dsh-shell": "0.1.5-rc.1",
145
- "@deepseek-ai/dsh-system-prompt": "0.1.5-rc.1",
146
- "@deepseek-ai/dsh-tools": "0.1.5-rc.1",
136
+ "@deepseek-ai/cordis": "~4.0.4",
137
+ "@deepseek-ai/dsh-agent": "0.1.7-rc.2",
138
+ "@deepseek-ai/dsh-attachment": "0.1.7-rc.2",
139
+ "@deepseek-ai/dsh-attachment-local": "0.1.7-rc.2",
140
+ "@deepseek-ai/dsh-client-store": "0.1.7-rc.2",
141
+ "@deepseek-ai/dsh-client-ui-model-selection": "0.1.7-rc.2",
142
+ "@deepseek-ai/dsh-credentials": "0.1.7-rc.2",
143
+ "@deepseek-ai/dsh-fs": "0.1.7-rc.2",
144
+ "@deepseek-ai/dsh-host-webserver": "0.1.7-rc.2",
145
+ "@deepseek-ai/dsh-llm": "0.1.7-rc.2",
146
+ "@deepseek-ai/dsh-sandbox": "0.1.7-rc.2",
147
+ "@deepseek-ai/dsh-sandbox-policy": "0.1.7-rc.2",
148
+ "@deepseek-ai/dsh-scope": "0.1.7-rc.2",
149
+ "@deepseek-ai/dsh-session": "0.1.7-rc.2",
150
+ "@deepseek-ai/dsh-shell": "0.1.7-rc.2",
151
+ "@deepseek-ai/dsh-system-prompt": "0.1.7-rc.2",
152
+ "@deepseek-ai/dsh-tools": "0.1.7-rc.2",
153
+ "@deepseek-ai/dsh-web-frontend": "0.1.7-rc.2",
147
154
  "@types/node": "^22.20.0",
148
155
  "@types/proxy-from-env": "1.0.4",
149
156
  "@types/ws": "8.18.1",
157
+ "immer": "10.1.1",
150
158
  "sharp": "^0.35.4",
151
159
  "typescript": "^6.0.3",
152
- "vitest": "^4.1.8"
160
+ "vitest": "^4.1.8",
161
+ "zustand": "4.4.7"
153
162
  },
154
163
  "peerDependenciesMeta": {
155
164
  "@deepseek-ai/dsh-tools": {
@@ -170,10 +179,5 @@
170
179
  "@deepseek-ai/dsh-sandbox-policy": {
171
180
  "optional": true
172
181
  }
173
- },
174
- "scripts": {
175
- "build": "tsc",
176
- "typecheck": "tsc --noEmit",
177
- "test": "vitest run"
178
182
  }
179
183
  }