codex-chatgpt-control 0.1.0-alpha.1

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.
Files changed (179) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE +21 -0
  3. package/README.md +54 -0
  4. package/contracts/v1/fixtures/backend-capabilities.json +54 -0
  5. package/contracts/v1/fixtures/backend-error-event-missing-stream-input.json +10 -0
  6. package/contracts/v1/fixtures/backend-error-missing-run-input.json +10 -0
  7. package/contracts/v1/fixtures/backend-runner-plan-request.json +12 -0
  8. package/contracts/v1/fixtures/backend-version.json +10 -0
  9. package/contracts/v1/fixtures/command-descriptors.json +803 -0
  10. package/contracts/v1/fixtures/describe-runner-run.json +31 -0
  11. package/contracts/v1/fixtures/doctor-bridge-upload.json +34 -0
  12. package/contracts/v1/fixtures/help-root.json +6 -0
  13. package/contracts/v1/fixtures/named-plan-two-turn.json +39 -0
  14. package/contracts/v1/fixtures/output-json-parse-success.json +115 -0
  15. package/contracts/v1/fixtures/primitive-bootstrap-blocker.json +43 -0
  16. package/contracts/v1/fixtures/report-redaction-default.json +16 -0
  17. package/contracts/v1/fixtures/reports-create-redacted.json +16 -0
  18. package/contracts/v1/fixtures/reports-summarize-redacted.json +26 -0
  19. package/contracts/v1/fixtures/responses-basic-success.json +27 -0
  20. package/contracts/v1/fixtures/responses-hidden-instructions-unsupported.json +22 -0
  21. package/contracts/v1/fixtures/responses-unknown-field-unsupported.json +22 -0
  22. package/contracts/v1/fixtures/responses-unsupported-previous-response-id.json +22 -0
  23. package/contracts/v1/fixtures/responses-unsupported-temperature.json +22 -0
  24. package/contracts/v1/fixtures/run-basic-success.json +36 -0
  25. package/contracts/v1/fixtures/run-browser-bridge-blocker.json +180 -0
  26. package/contracts/v1/fixtures/run-file-attach-success.json +63 -0
  27. package/contracts/v1/fixtures/run-report-redacted.json +52 -0
  28. package/contracts/v1/fixtures/run-selector-drift-blocker.json +60 -0
  29. package/contracts/v1/fixtures/run-timeout-partial.json +81 -0
  30. package/contracts/v1/fixtures/run-two-turn-success.json +83 -0
  31. package/contracts/v1/fixtures/run-upload-permission-blocker.json +56 -0
  32. package/contracts/v1/fixtures/runner-budget-blocker.json +106 -0
  33. package/contracts/v1/fixtures/runner-full-agent-config.json +50 -0
  34. package/contracts/v1/fixtures/runner-input-items-and-files-plan.json +53 -0
  35. package/contracts/v1/fixtures/runner-metadata-only-plan.json +31 -0
  36. package/contracts/v1/fixtures/runner-visible-prefix-plan.json +28 -0
  37. package/contracts/v1/fixtures/runner-visible-setup-plan.json +37 -0
  38. package/contracts/v1/fixtures/stream-basic.ndjson +3 -0
  39. package/contracts/v1/fixtures/stream-blocked.ndjson +2 -0
  40. package/contracts/v1/fixtures/stream-submitted-completed.ndjson +3 -0
  41. package/contracts/v1/fixtures/workflow-ask-success.json +67 -0
  42. package/contracts/v1/manifest.json +210 -0
  43. package/contracts/v1/parity-suite.json +635 -0
  44. package/contracts/v1/schemas/agent.schema.json +52 -0
  45. package/contracts/v1/schemas/backend-event.schema.json +74 -0
  46. package/contracts/v1/schemas/backend-request.schema.json +59 -0
  47. package/contracts/v1/schemas/backend-response.schema.json +42 -0
  48. package/contracts/v1/schemas/capabilities.schema.json +33 -0
  49. package/contracts/v1/schemas/command-descriptor.schema.json +31 -0
  50. package/contracts/v1/schemas/command-result.schema.json +99 -0
  51. package/contracts/v1/schemas/manifest.schema.json +69 -0
  52. package/contracts/v1/schemas/response.schema.json +51 -0
  53. package/contracts/v1/schemas/run-result.schema.json +138 -0
  54. package/contracts/v1/schemas/sequence-plan.schema.json +44 -0
  55. package/contracts/v1/schemas/stream-event.schema.json +25 -0
  56. package/dist/codex-chatgpt-control-backend.mjs +5284 -0
  57. package/dist/codex-chatgpt-control.bundle.mjs +6013 -0
  58. package/dist/src/backend/client.d.ts +129 -0
  59. package/dist/src/backend/client.js +415 -0
  60. package/dist/src/backend/protocol.d.ts +74 -0
  61. package/dist/src/backend/protocol.js +128 -0
  62. package/dist/src/backend/session.d.ts +11 -0
  63. package/dist/src/backend/session.js +223 -0
  64. package/dist/src/backend/stdio-server.d.ts +9 -0
  65. package/dist/src/backend/stdio-server.js +88 -0
  66. package/dist/src/browser/attach.d.ts +8 -0
  67. package/dist/src/browser/attach.js +456 -0
  68. package/dist/src/browser/clipboard.d.ts +2 -0
  69. package/dist/src/browser/clipboard.js +26 -0
  70. package/dist/src/browser/downloads.d.ts +7 -0
  71. package/dist/src/browser/downloads.js +29 -0
  72. package/dist/src/browser/page-state.d.ts +17 -0
  73. package/dist/src/browser/page-state.js +66 -0
  74. package/dist/src/client.d.ts +167 -0
  75. package/dist/src/client.js +666 -0
  76. package/dist/src/commands/confirmations.d.ts +9 -0
  77. package/dist/src/commands/confirmations.js +33 -0
  78. package/dist/src/commands/context.d.ts +2 -0
  79. package/dist/src/commands/context.js +32 -0
  80. package/dist/src/commands/doctor.d.ts +16 -0
  81. package/dist/src/commands/doctor.js +120 -0
  82. package/dist/src/commands/files.d.ts +4 -0
  83. package/dist/src/commands/files.js +314 -0
  84. package/dist/src/commands/helpers.d.ts +16 -0
  85. package/dist/src/commands/helpers.js +128 -0
  86. package/dist/src/commands/messages.d.ts +16 -0
  87. package/dist/src/commands/messages.js +538 -0
  88. package/dist/src/commands/modes.d.ts +9 -0
  89. package/dist/src/commands/modes.js +323 -0
  90. package/dist/src/commands/output.d.ts +5 -0
  91. package/dist/src/commands/output.js +31 -0
  92. package/dist/src/commands/registry.d.ts +17 -0
  93. package/dist/src/commands/registry.js +275 -0
  94. package/dist/src/commands/reports.d.ts +14 -0
  95. package/dist/src/commands/reports.js +47 -0
  96. package/dist/src/commands/response-actions.d.ts +2 -0
  97. package/dist/src/commands/response-actions.js +141 -0
  98. package/dist/src/commands/sequence.d.ts +9 -0
  99. package/dist/src/commands/sequence.js +228 -0
  100. package/dist/src/commands/session.d.ts +2 -0
  101. package/dist/src/commands/session.js +25 -0
  102. package/dist/src/commands/threads.d.ts +6 -0
  103. package/dist/src/commands/threads.js +300 -0
  104. package/dist/src/dom/fixtures.d.ts +1 -0
  105. package/dist/src/dom/fixtures.js +8 -0
  106. package/dist/src/dom/menus.d.ts +8 -0
  107. package/dist/src/dom/menus.js +40 -0
  108. package/dist/src/dom/message-format.d.ts +30 -0
  109. package/dist/src/dom/message-format.js +593 -0
  110. package/dist/src/dom/messages.d.ts +40 -0
  111. package/dist/src/dom/messages.js +127 -0
  112. package/dist/src/dom/selectors.d.ts +20 -0
  113. package/dist/src/dom/selectors.js +72 -0
  114. package/dist/src/dom/visible-text.d.ts +5 -0
  115. package/dist/src/dom/visible-text.js +27 -0
  116. package/dist/src/errors.d.ts +32 -0
  117. package/dist/src/errors.js +127 -0
  118. package/dist/src/index.d.ts +34 -0
  119. package/dist/src/index.js +34 -0
  120. package/dist/src/logger.d.ts +15 -0
  121. package/dist/src/logger.js +25 -0
  122. package/dist/src/runner/agent.d.ts +2 -0
  123. package/dist/src/runner/agent.js +17 -0
  124. package/dist/src/runner/index.d.ts +7 -0
  125. package/dist/src/runner/index.js +7 -0
  126. package/dist/src/runner/interruptions.d.ts +3 -0
  127. package/dist/src/runner/interruptions.js +65 -0
  128. package/dist/src/runner/responses.d.ts +30 -0
  129. package/dist/src/runner/responses.js +171 -0
  130. package/dist/src/runner/result.d.ts +3 -0
  131. package/dist/src/runner/result.js +128 -0
  132. package/dist/src/runner/resume.d.ts +11 -0
  133. package/dist/src/runner/resume.js +26 -0
  134. package/dist/src/runner/stream.d.ts +13 -0
  135. package/dist/src/runner/stream.js +66 -0
  136. package/dist/src/runner/types.d.ts +264 -0
  137. package/dist/src/runner/types.js +1 -0
  138. package/dist/src/safety/blockers.d.ts +7 -0
  139. package/dist/src/safety/blockers.js +60 -0
  140. package/dist/src/safety/redaction.d.ts +2 -0
  141. package/dist/src/safety/redaction.js +18 -0
  142. package/dist/src/safety/report-redaction.d.ts +8 -0
  143. package/dist/src/safety/report-redaction.js +74 -0
  144. package/dist/src/safety/risk.d.ts +26 -0
  145. package/dist/src/safety/risk.js +28 -0
  146. package/dist/src/scripts/backend-server.d.ts +2 -0
  147. package/dist/src/scripts/backend-server.js +7 -0
  148. package/dist/src/scripts/check-parity-suite.d.ts +1 -0
  149. package/dist/src/scripts/check-parity-suite.js +10 -0
  150. package/dist/src/scripts/continue-thread.d.ts +27 -0
  151. package/dist/src/scripts/continue-thread.js +388 -0
  152. package/dist/src/scripts/live-smoke/harness.d.ts +11 -0
  153. package/dist/src/scripts/live-smoke/harness.js +151 -0
  154. package/dist/src/scripts/live-smoke/scenarios.d.ts +3 -0
  155. package/dist/src/scripts/live-smoke/scenarios.js +648 -0
  156. package/dist/src/scripts/live-smoke/types.d.ts +53 -0
  157. package/dist/src/scripts/live-smoke/types.js +1 -0
  158. package/dist/src/scripts/live-smoke-module.d.ts +3 -0
  159. package/dist/src/scripts/live-smoke-module.js +2 -0
  160. package/dist/src/scripts/live-smoke.d.ts +1 -0
  161. package/dist/src/scripts/live-smoke.js +54 -0
  162. package/dist/src/scripts/smoke-attach-file.d.ts +1 -0
  163. package/dist/src/scripts/smoke-attach-file.js +39 -0
  164. package/dist/src/scripts/smoke-hi.d.ts +1 -0
  165. package/dist/src/scripts/smoke-hi.js +25 -0
  166. package/dist/src/scripts/smoke-search-open-read.d.ts +1 -0
  167. package/dist/src/scripts/smoke-search-open-read.js +41 -0
  168. package/dist/src/testing/parity-suite.d.ts +12 -0
  169. package/dist/src/testing/parity-suite.js +213 -0
  170. package/dist/src/types.d.ts +495 -0
  171. package/dist/src/types.js +1 -0
  172. package/package.json +81 -0
  173. package/references/agents-runner.md +32 -0
  174. package/references/backend-protocol.md +238 -0
  175. package/references/python-parity.md +168 -0
  176. package/references/responses-adapter.md +28 -0
  177. package/references/safety.md +14 -0
  178. package/references/streaming.md +15 -0
  179. package/references/troubleshooting.md +91 -0
@@ -0,0 +1,264 @@
1
+ import type { AttachedFile, CommandResult, CommandStatus, CopyResponseArgs, DownloadLatestArgs, BootstrapArgs, ReadLatestArgs, ResponseCaptureFidelity, ResponseCaptureSource, ResponseFormat, SequencePlan, SelectToolArgs, SetModeArgs, ThreadTarget, WaitArgs } from "../types.js";
2
+ import type { RunReportOptions } from "../commands/reports.js";
3
+ import type { ChatGPTRunStream } from "./stream.js";
4
+ export type ChatGPTThreadSelector = {
5
+ type: "new";
6
+ } | {
7
+ type: "current";
8
+ } | {
9
+ type: "url";
10
+ url: string;
11
+ } | {
12
+ type: "conversationId";
13
+ conversationId: string;
14
+ } | {
15
+ type: "conversation_id";
16
+ conversationId: string;
17
+ } | {
18
+ type: "search";
19
+ query: string;
20
+ select?: "first" | {
21
+ index: number;
22
+ } | {
23
+ title: string;
24
+ };
25
+ limit?: number;
26
+ } | {
27
+ type: "title";
28
+ title: string;
29
+ };
30
+ export type ChatGPTAttachmentInput = {
31
+ path: string;
32
+ description?: string;
33
+ sensitivity?: "normal" | "sensitive" | "highly_sensitive";
34
+ };
35
+ export type ChatGPTResponseReadOptions = ReadLatestArgs;
36
+ export type ChatGPTVisibleModePreference = SetModeArgs & {
37
+ required?: boolean;
38
+ ifUnavailable?: "continue" | "block" | "skip";
39
+ };
40
+ export type ChatGPTVisibleToolPreference = SelectToolArgs & {
41
+ required?: boolean;
42
+ ifUnavailable?: "continue" | "block" | "skip";
43
+ };
44
+ export type ChatGPTRunDefaults = {
45
+ thread?: ChatGPTThreadSelector | ThreadTarget;
46
+ existingTab?: BootstrapArgs["existingTab"];
47
+ preferExistingTab?: boolean;
48
+ mode?: ChatGPTVisibleModePreference;
49
+ wait?: boolean | WaitArgs;
50
+ read?: boolean | ReadLatestArgs;
51
+ report?: boolean | RunReportOptions;
52
+ };
53
+ export type ChatGPTOutputSpec<TOutput = string> = {
54
+ parse?: "text" | "json";
55
+ onParseError?: "return_text" | "unsupported" | "error";
56
+ sample?: TOutput;
57
+ };
58
+ export type ChatGPTBrowserTool = {
59
+ name: string;
60
+ command: string;
61
+ risk?: "low" | "medium" | "high";
62
+ };
63
+ export type ChatGPTGuardrail = {
64
+ name: string;
65
+ scope: "input" | "plan" | "step" | "output" | "report";
66
+ };
67
+ export type ChatGPTAgentConfig<TOutput = string> = {
68
+ name: string;
69
+ instructions?: string;
70
+ instructionsMode?: "visible_prefix" | "visible_setup_message" | "metadata_only";
71
+ defaults?: Partial<ChatGPTRunDefaults>;
72
+ tools?: ChatGPTBrowserTool[];
73
+ guardrails?: ChatGPTGuardrail[];
74
+ output?: ChatGPTOutputSpec<TOutput>;
75
+ metadata?: Record<string, unknown>;
76
+ };
77
+ export type ChatGPTAgent<TOutput = string> = {
78
+ readonly kind: "chatgpt_browser_agent";
79
+ readonly name: string;
80
+ readonly instructions?: string;
81
+ readonly instructionsMode: "visible_prefix" | "visible_setup_message" | "metadata_only";
82
+ readonly defaults: Partial<ChatGPTRunDefaults>;
83
+ readonly tools: ChatGPTBrowserTool[];
84
+ readonly guardrails: ChatGPTGuardrail[];
85
+ readonly output?: ChatGPTOutputSpec<TOutput>;
86
+ readonly metadata?: Record<string, unknown>;
87
+ };
88
+ export type ChatGPTInputItem = {
89
+ type: "input_text";
90
+ text: string;
91
+ role?: "user";
92
+ } | {
93
+ type: "input_file";
94
+ path: string;
95
+ description?: string;
96
+ } | {
97
+ type: "visible_instruction";
98
+ text: string;
99
+ };
100
+ export type ChatGPTRunInput = string | {
101
+ input: string | ChatGPTInputItem[];
102
+ thread?: ChatGPTThreadSelector;
103
+ existingTab?: BootstrapArgs["existingTab"];
104
+ preferExistingTab?: boolean;
105
+ attachments?: ChatGPTAttachmentInput[];
106
+ mode?: ChatGPTVisibleModePreference;
107
+ tools?: ChatGPTVisibleToolPreference[];
108
+ response?: ChatGPTResponseReadOptions;
109
+ download?: DownloadLatestArgs | false;
110
+ copy?: CopyResponseArgs | false;
111
+ report?: boolean | RunReportOptions;
112
+ metadata?: Record<string, unknown>;
113
+ };
114
+ export type ChatGPTThreadRef = {
115
+ url?: string;
116
+ conversationId?: string;
117
+ title?: string;
118
+ };
119
+ export type AttachedFileSummary = Pick<AttachedFile, "path" | "name" | "bytes">;
120
+ export type DownloadedFileSummary = {
121
+ path: string;
122
+ suggestedFilename?: string;
123
+ bytes: number;
124
+ };
125
+ export type ChatGPTRunItem = {
126
+ type: "thread.opened";
127
+ thread: ChatGPTThreadRef;
128
+ } | {
129
+ type: "mode.selected";
130
+ requested?: string;
131
+ selected?: string;
132
+ candidates?: string[];
133
+ } | {
134
+ type: "tool.selected";
135
+ requested: string;
136
+ selected?: string;
137
+ candidates?: string[];
138
+ } | {
139
+ type: "file.attached";
140
+ file: AttachedFileSummary;
141
+ } | {
142
+ type: "message.submitted";
143
+ role: "user";
144
+ preview: string;
145
+ redacted: boolean;
146
+ } | {
147
+ type: "message.completed";
148
+ role: "assistant";
149
+ output_text?: string;
150
+ format: ResponseFormat;
151
+ source?: ResponseCaptureSource;
152
+ fidelity?: ResponseCaptureFidelity;
153
+ } | {
154
+ type: "file.downloaded";
155
+ file: DownloadedFileSummary;
156
+ } | {
157
+ type: "approval.required";
158
+ interruption: ChatGPTInterruption;
159
+ } | {
160
+ type: "run.blocked";
161
+ blocker: ChatGPTCommandBlocker;
162
+ };
163
+ export type RemediationStep = {
164
+ label: string;
165
+ instruction: string;
166
+ userActionRequired: boolean;
167
+ };
168
+ export type VisibleCandidate = {
169
+ label: string;
170
+ role?: string;
171
+ };
172
+ export type ChatGPTCommandBlocker = NonNullable<CommandResult["blocker"]> & {
173
+ code?: string;
174
+ fieldPath?: string;
175
+ remediation?: RemediationStep[];
176
+ candidates?: VisibleCandidate[];
177
+ resumable?: boolean;
178
+ };
179
+ export type ChatGPTInterruption = {
180
+ id: string;
181
+ type: "approval_required" | "permission_required" | "login_required" | "selector_drift" | "rate_limit" | "captcha" | "unsupported" | "timeout";
182
+ status: CommandStatus;
183
+ blocker?: ChatGPTCommandBlocker;
184
+ command?: string;
185
+ fieldPath?: string;
186
+ message: string;
187
+ fix?: {
188
+ summary: string;
189
+ steps: string[];
190
+ };
191
+ resume: {
192
+ supported: boolean;
193
+ stateId?: string;
194
+ reason?: string;
195
+ };
196
+ };
197
+ export type ChatGPTInterruptionDecision = {
198
+ id: string;
199
+ decision: "approve";
200
+ confirm: ChatGPTConfirmation;
201
+ } | {
202
+ id: string;
203
+ decision: "reject";
204
+ message?: string;
205
+ };
206
+ export type ChatGPTConfirmation = {
207
+ targetKind: "file_upload" | "download" | "thread" | "shared_link" | "third_party_app" | "account_setting" | "workspace_setting" | "external_action" | "quota_consuming_tool";
208
+ targetDisplayName: string;
209
+ action: string;
210
+ risks?: string[];
211
+ understood: true;
212
+ };
213
+ export type ChatGPTRunState = {
214
+ id: string;
215
+ resumable: boolean;
216
+ thread?: ChatGPTThreadRef;
217
+ nextStepId?: string;
218
+ };
219
+ export type ChatGPTRunData<TOutput = string> = {
220
+ finalOutput?: TOutput;
221
+ outputText: string;
222
+ thread?: ChatGPTThreadRef;
223
+ downloads?: DownloadedFileSummary[];
224
+ reportPath?: string;
225
+ };
226
+ export type ChatGPTRunResult<TOutput = string> = CommandResult<ChatGPTRunData<TOutput>> & {
227
+ finalOutput?: TOutput;
228
+ output_text: string;
229
+ output: ChatGPTRunItem[];
230
+ newItems: ChatGPTRunItem[];
231
+ interruptions: ChatGPTInterruption[];
232
+ state: ChatGPTRunState;
233
+ activeAgentName: string;
234
+ lastAgentName: string;
235
+ };
236
+ export type ChatGPTRunner = {
237
+ run<TOutput = string>(agent: ChatGPTAgent<TOutput>, input: ChatGPTRunInput, options: {
238
+ stream: true;
239
+ }): ChatGPTRunStream<TOutput>;
240
+ run<TOutput = string>(agent: ChatGPTAgent<TOutput>, input: ChatGPTRunInput, options?: {
241
+ stream?: false;
242
+ }): Promise<ChatGPTRunResult<TOutput>>;
243
+ plan<TOutput = string>(agent: ChatGPTAgent<TOutput>, input: ChatGPTRunInput): SequencePlan;
244
+ };
245
+ export type ChatGPTResponse = {
246
+ id: string;
247
+ object: "chatgpt.browser.response";
248
+ created_at: number;
249
+ status: CommandStatus;
250
+ output_text: string;
251
+ output: ChatGPTRunItem[];
252
+ browser_control: {
253
+ visibleUi: true;
254
+ resultStatus: CommandStatus;
255
+ thread?: ChatGPTThreadRef;
256
+ reportPath?: string;
257
+ unsupported?: UnsupportedField[];
258
+ };
259
+ };
260
+ export type UnsupportedField = {
261
+ path: string;
262
+ reason: string;
263
+ alternative?: string;
264
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { BlockerKind } from "../types.js";
2
+ export type ClassifiedBlocker = {
3
+ kind: BlockerKind;
4
+ message: string;
5
+ visibleText?: string;
6
+ };
7
+ export declare function classifyVisibleText(text: string): ClassifiedBlocker | undefined;
@@ -0,0 +1,60 @@
1
+ import { compactVisibleText } from "./redaction.js";
2
+ const RULES = [
3
+ {
4
+ kind: "login_required",
5
+ message: "ChatGPT requires the user to sign in before continuing.",
6
+ patterns: [/\blog\s?in\b/i, /\bsign\s?in\b/i, /\bwelcome back\b/i]
7
+ },
8
+ {
9
+ kind: "captcha",
10
+ message: "ChatGPT is showing a captcha or suspicious-activity challenge.",
11
+ patterns: [/\bcaptcha\b/i, /verify (?:you are|that you are) human/i, /suspicious activity/i]
12
+ },
13
+ {
14
+ kind: "rate_limit",
15
+ message: "ChatGPT is rate limited or out of usage for this account.",
16
+ patterns: [/usage limit/i, /rate limit/i, /try again later/i, /too many requests/i]
17
+ },
18
+ {
19
+ kind: "permission",
20
+ message: "File upload permission is required. Ask the user to enable both: Codex Settings > Computer Use > Chrome > Permissions > Uploads, and Chrome chrome://extensions > Codex extension > Details > Allow access to file URLs.",
21
+ patterns: [/allow access to file urls/i, /file upload permission/i, /fileChooser\.setFiles/i]
22
+ },
23
+ {
24
+ kind: "permission",
25
+ message: "A browser or ChatGPT permission is required before continuing.",
26
+ patterns: [/permission denied/i, /browser blocked/i]
27
+ },
28
+ {
29
+ kind: "upload_failed",
30
+ message: "ChatGPT reported a file upload failure.",
31
+ patterns: [/upload failed/i, /could(?: not|n't) upload/i, /unsupported file/i, /file is too large/i]
32
+ },
33
+ {
34
+ kind: "download_unavailable",
35
+ message: "No downloadable file or download control is visible.",
36
+ patterns: [/download unavailable/i, /no download/i]
37
+ },
38
+ {
39
+ kind: "not_found",
40
+ message: "The requested ChatGPT conversation or page was not found.",
41
+ patterns: [/conversation not found/i, /404/i, /page not found/i]
42
+ }
43
+ ];
44
+ export function classifyVisibleText(text) {
45
+ const visibleText = compactVisibleText(text);
46
+ const lowerable = visibleText.length > 0 ? visibleText : text;
47
+ for (const rule of RULES) {
48
+ if (rule.patterns.some(pattern => pattern.test(lowerable))) {
49
+ return { kind: rule.kind, message: rule.message, visibleText };
50
+ }
51
+ }
52
+ if (/\b(confirm|continue|cancel|dismiss)\b/i.test(lowerable) && /\bdialog\b|\bmodal\b/i.test(lowerable)) {
53
+ return {
54
+ kind: "modal",
55
+ message: "ChatGPT is showing a modal dialog that may require user action.",
56
+ visibleText
57
+ };
58
+ }
59
+ return undefined;
60
+ }
@@ -0,0 +1,2 @@
1
+ export declare function redactSensitiveText(text: string): string;
2
+ export declare function compactVisibleText(text: string, maxLength?: number): string;
@@ -0,0 +1,18 @@
1
+ const EMAIL_RE = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi;
2
+ const PHONE_RE = /\b(?:\+?1[-.\s]?)?(?:\(?\d{3}\)?[-.\s]?)\d{3}[-.\s]?\d{4}\b/g;
3
+ const TOKEN_RE = /\b[A-Za-z0-9_-]{32,}\b/g;
4
+ const PATH_RE = /(?:\/Users\/|\/home\/|\/example\/user\/)[^\s"'<>]+/g;
5
+ export function redactSensitiveText(text) {
6
+ return text
7
+ .replace(EMAIL_RE, "[redacted-email]")
8
+ .replace(PHONE_RE, "[redacted-phone]")
9
+ .replace(PATH_RE, "[redacted-path]")
10
+ .replace(TOKEN_RE, "[redacted-token]");
11
+ }
12
+ export function compactVisibleText(text, maxLength = 1000) {
13
+ const compacted = redactSensitiveText(text.replace(/\s+/g, " ").trim());
14
+ if (compacted.length <= maxLength) {
15
+ return compacted;
16
+ }
17
+ return `${compacted.slice(0, maxLength - 1)}...`;
18
+ }
@@ -0,0 +1,8 @@
1
+ export type ReportRedactionOptions = {
2
+ includeContent?: boolean;
3
+ maxPreviewChars?: number;
4
+ maxDepth?: number;
5
+ maxArrayItems?: number;
6
+ maxObjectEntries?: number;
7
+ };
8
+ export declare function redactReportValue(value: unknown, options?: ReportRedactionOptions): unknown;
@@ -0,0 +1,74 @@
1
+ import { compactVisibleText, redactSensitiveText } from "./redaction.js";
2
+ const DEFAULT_MAX_PREVIEW_CHARS = 240;
3
+ const DEFAULT_MAX_DEPTH = 8;
4
+ const DEFAULT_MAX_ARRAY_ITEMS = 40;
5
+ const DEFAULT_MAX_OBJECT_ENTRIES = 80;
6
+ export function redactReportValue(value, options = {}) {
7
+ return redactValue(value, normalizeOptions(options), 0, new WeakSet(), undefined);
8
+ }
9
+ function redactValue(value, options, depth, seen, key) {
10
+ if (value === undefined || value === null)
11
+ return value;
12
+ if (typeof value === "string") {
13
+ if (!options.includeContent && key !== undefined && isSafeControlStringKey(key)) {
14
+ return value;
15
+ }
16
+ if (!options.includeContent)
17
+ return `[redacted:${value.length} chars]`;
18
+ return compactVisibleText(redactSensitiveText(value), options.maxPreviewChars);
19
+ }
20
+ if (typeof value === "number" || typeof value === "boolean")
21
+ return value;
22
+ if (typeof value !== "object")
23
+ return redactSensitiveText(String(value));
24
+ if (seen.has(value))
25
+ return "[redacted:cycle]";
26
+ if (depth >= options.maxDepth)
27
+ return "[redacted:max-depth]";
28
+ if (!options.includeContent && key !== undefined && isHeavyContentKey(key)) {
29
+ return summarizeHeavyValue(value);
30
+ }
31
+ seen.add(value);
32
+ try {
33
+ if (Array.isArray(value)) {
34
+ const items = value.slice(0, options.maxArrayItems)
35
+ .map(item => redactValue(item, options, depth + 1, seen, key));
36
+ if (value.length > options.maxArrayItems) {
37
+ items.push(`[redacted:${value.length - options.maxArrayItems} more items]`);
38
+ }
39
+ return items;
40
+ }
41
+ const entries = Object.entries(value);
42
+ const kept = entries.slice(0, options.maxObjectEntries).map(([childKey, child]) => [
43
+ childKey,
44
+ redactValue(child, options, depth + 1, seen, childKey)
45
+ ]);
46
+ if (entries.length > options.maxObjectEntries) {
47
+ kept.push(["__redactedMoreEntries", entries.length - options.maxObjectEntries]);
48
+ }
49
+ return Object.fromEntries(kept);
50
+ }
51
+ finally {
52
+ seen.delete(value);
53
+ }
54
+ }
55
+ function normalizeOptions(options) {
56
+ return {
57
+ includeContent: options.includeContent === true,
58
+ maxPreviewChars: options.maxPreviewChars ?? DEFAULT_MAX_PREVIEW_CHARS,
59
+ maxDepth: options.maxDepth ?? DEFAULT_MAX_DEPTH,
60
+ maxArrayItems: options.maxArrayItems ?? DEFAULT_MAX_ARRAY_ITEMS,
61
+ maxObjectEntries: options.maxObjectEntries ?? DEFAULT_MAX_OBJECT_ENTRIES
62
+ };
63
+ }
64
+ function isHeavyContentKey(key) {
65
+ return /^(text|markdown|html|visibleText|normalizedText|responseText|output_text|outputText|finalOutput|prompt|blocks|tables|codeBlocks|dataPreview)$/i.test(key);
66
+ }
67
+ function summarizeHeavyValue(value) {
68
+ if (Array.isArray(value))
69
+ return `[redacted-array:${value.length} items]`;
70
+ return "[redacted-object]";
71
+ }
72
+ function isSafeControlStringKey(key) {
73
+ return /^(schemaVersion|status|startedAt|endedAt|createdAt|timestamp|requiredFailures)$/i.test(key);
74
+ }
@@ -0,0 +1,26 @@
1
+ export type RiskLevel = "low" | "medium" | "high";
2
+ export declare const commandRisk: {
3
+ readonly "session.bootstrap": "low";
4
+ readonly "threads.search": "medium";
5
+ readonly "threads.open": "medium";
6
+ readonly "threads.new": "low";
7
+ readonly "messages.compose": "low";
8
+ readonly "messages.submit": "medium";
9
+ readonly "messages.ask": "medium";
10
+ readonly "messages.wait": "low";
11
+ readonly "messages.readLatest": "medium";
12
+ readonly "messages.waitAndRead": "medium";
13
+ readonly "files.attach": "medium";
14
+ readonly "files.downloadLatest": "medium";
15
+ readonly "response.copy": "medium";
16
+ readonly "modes.set": "medium";
17
+ readonly "tools.select": "medium";
18
+ readonly "threads.delete": "high";
19
+ readonly "threads.archive": "high";
20
+ readonly "threads.share": "high";
21
+ readonly "settings.change": "high";
22
+ readonly "apps.connect": "high";
23
+ };
24
+ export type KnownRiskCommand = keyof typeof commandRisk;
25
+ export declare function riskForCommand(command: string): RiskLevel;
26
+ export declare function isHighRiskCommand(command: string): boolean;
@@ -0,0 +1,28 @@
1
+ export const commandRisk = {
2
+ "session.bootstrap": "low",
3
+ "threads.search": "medium",
4
+ "threads.open": "medium",
5
+ "threads.new": "low",
6
+ "messages.compose": "low",
7
+ "messages.submit": "medium",
8
+ "messages.ask": "medium",
9
+ "messages.wait": "low",
10
+ "messages.readLatest": "medium",
11
+ "messages.waitAndRead": "medium",
12
+ "files.attach": "medium",
13
+ "files.downloadLatest": "medium",
14
+ "response.copy": "medium",
15
+ "modes.set": "medium",
16
+ "tools.select": "medium",
17
+ "threads.delete": "high",
18
+ "threads.archive": "high",
19
+ "threads.share": "high",
20
+ "settings.change": "high",
21
+ "apps.connect": "high"
22
+ };
23
+ export function riskForCommand(command) {
24
+ return commandRisk[command] ?? "high";
25
+ }
26
+ export function isHighRiskCommand(command) {
27
+ return riskForCommand(command) === "high";
28
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { runBackendStdioServer } from "../backend/stdio-server.js";
3
+ await runBackendStdioServer({
4
+ input: process.stdin,
5
+ output: process.stdout,
6
+ error: process.stderr
7
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import { validateParitySuite } from "../testing/parity-suite.js";
2
+ const report = validateParitySuite();
3
+ console.log([
4
+ `Validated polyglot parity suite:`,
5
+ `- evidence mode: ${report.evidenceMode}`,
6
+ `- ${report.surfaceCount} surfaces`,
7
+ `- ${report.fixtureCount} contract fixtures`,
8
+ `- ${report.commandCount} backend commands`,
9
+ `- ${report.gateCount} deterministic gates`
10
+ ].join("\n"));
@@ -0,0 +1,27 @@
1
+ import { type ChatGPTClient, type WorkflowThread } from "../client.js";
2
+ import type { CommandResult, ExistingTabPolicy, ResponseFormat } from "../types.js";
3
+ export declare const CONTINUE_THREAD_USAGE: string;
4
+ export type ContinueThreadOptions = {
5
+ target?: string;
6
+ existing?: ExistingTabPolicy;
7
+ prompt?: string;
8
+ format: ResponseFormat;
9
+ maxChars?: number;
10
+ timeoutMs?: number;
11
+ stableMs?: number;
12
+ };
13
+ export type ContinueThreadClient = Pick<ChatGPTClient, "askInThread" | "openThread" | "readLatest" | "session">;
14
+ export type ContinueThreadSelector = Exclude<WorkflowThread, {
15
+ type: "new";
16
+ }>;
17
+ export declare class ContinueThreadUsageError extends Error {
18
+ readonly exitCode: number;
19
+ constructor(message: string, exitCode?: number);
20
+ }
21
+ export declare function parseContinueThreadCliArgs(argv: string[], env?: Record<string, string | undefined>): ContinueThreadOptions;
22
+ export declare function threadSelectorFromTarget(target: string, args?: {
23
+ limit?: number;
24
+ }): ContinueThreadSelector;
25
+ export declare function runContinueThread(client: ContinueThreadClient, options: ContinueThreadOptions): Promise<CommandResult<unknown>>;
26
+ export declare function renderContinueThreadOutput(result: CommandResult<unknown>): Record<string, unknown>;
27
+ export declare function main(argv?: string[], env?: Record<string, string | undefined>): Promise<number>;