codex-chatgpt-control 0.5.1-alpha.2 → 0.5.1-alpha.3
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 +15 -0
- package/README.md +27 -0
- package/contracts/v1/fixtures/backend-capabilities.json +40 -1
- package/contracts/v1/fixtures/backend-compatibility.json +21 -0
- package/contracts/v1/fixtures/backend-version.json +6 -1
- package/contracts/v1/fixtures/operation-action-prepared-event.json +42 -0
- package/contracts/v1/fixtures/operation-action.json +13 -0
- package/contracts/v1/fixtures/operation-artifact-receipt.json +14 -0
- package/contracts/v1/fixtures/operation-artifact-transfer-intent-event.json +24 -0
- package/contracts/v1/fixtures/operation-artifact-transfer-receipt-event.json +26 -0
- package/contracts/v1/fixtures/operation-artifact-transfer-state.json +178 -0
- package/contracts/v1/fixtures/operation-blocker.json +10 -0
- package/contracts/v1/fixtures/operation-collect-request.json +17 -0
- package/contracts/v1/fixtures/operation-collect-result.json +42 -0
- package/contracts/v1/fixtures/operation-control-receipt.json +13 -0
- package/contracts/v1/fixtures/operation-control-request.json +18 -0
- package/contracts/v1/fixtures/operation-control-result.json +31 -0
- package/contracts/v1/fixtures/operation-event.json +18 -0
- package/contracts/v1/fixtures/operation-handle.json +10 -0
- package/contracts/v1/fixtures/operation-inspect-request.json +13 -0
- package/contracts/v1/fixtures/operation-inspect-result.json +205 -0
- package/contracts/v1/fixtures/operation-ownership-baseline-event.json +34 -0
- package/contracts/v1/fixtures/operation-receipt.json +32 -0
- package/contracts/v1/fixtures/operation-recovery-decision.json +7 -0
- package/contracts/v1/fixtures/operation-recovery-observation.json +12 -0
- package/contracts/v1/fixtures/operation-request.json +33 -0
- package/contracts/v1/fixtures/operation-state.json +144 -0
- package/contracts/v1/fixtures/operation-submission-witness-event.json +20 -0
- package/contracts/v1/fixtures/operation-submission-witness.json +11 -0
- package/contracts/v1/fixtures/operation-submit-result.json +16 -0
- package/contracts/v1/fixtures/operation-target-established-event.json +21 -0
- package/contracts/v1/manifest.json +155 -1
- package/contracts/v1/parity-suite.json +96 -1
- package/contracts/v1/schemas/backend-compatibility.schema.json +65 -0
- package/contracts/v1/schemas/backend-request.schema.json +8 -2
- package/contracts/v1/schemas/capabilities.schema.json +81 -1
- package/contracts/v1/schemas/manifest.schema.json +57 -0
- package/contracts/v1/schemas/operation-action.schema.json +155 -0
- package/contracts/v1/schemas/operation-artifact-receipt.schema.json +89 -0
- package/contracts/v1/schemas/operation-blocker.schema.json +70 -0
- package/contracts/v1/schemas/operation-collect-request.schema.json +46 -0
- package/contracts/v1/schemas/operation-collect-result.schema.json +74 -0
- package/contracts/v1/schemas/operation-control-receipt.schema.json +108 -0
- package/contracts/v1/schemas/operation-control-request.schema.json +111 -0
- package/contracts/v1/schemas/operation-control-result.schema.json +112 -0
- package/contracts/v1/schemas/operation-event.schema.json +756 -0
- package/contracts/v1/schemas/operation-handle.schema.json +55 -0
- package/contracts/v1/schemas/operation-inspect-request.schema.json +42 -0
- package/contracts/v1/schemas/operation-inspect-result.schema.json +1662 -0
- package/contracts/v1/schemas/operation-receipt.schema.json +153 -0
- package/contracts/v1/schemas/operation-recovery.schema.json +314 -0
- package/contracts/v1/schemas/operation-request.schema.json +158 -0
- package/contracts/v1/schemas/operation-state.schema.json +691 -0
- package/contracts/v1/schemas/operation-submission-witness.schema.json +48 -0
- package/contracts/v1/schemas/operation-submit-result.schema.json +94 -0
- package/contracts/v1/surface-drift-policy.json +50 -0
- package/contracts/v1/vectors/operation-request-digest-v1.json +43 -0
- package/dist/codex-chatgpt-control-backend.mjs +43897 -9094
- package/dist/codex-chatgpt-control.bundle.mjs +46360 -10262
- package/dist/src/backend/client.d.ts +103 -2
- package/dist/src/backend/client.js +1436 -97
- package/dist/src/backend/compatibility.d.ts +12 -0
- package/dist/src/backend/compatibility.js +208 -0
- package/dist/src/backend/protocol.d.ts +84 -1
- package/dist/src/backend/protocol.js +31 -5
- package/dist/src/backend/runtime-identity.d.ts +10 -0
- package/dist/src/backend/runtime-identity.js +141 -0
- package/dist/src/backend/session.d.ts +10 -2
- package/dist/src/backend/session.js +478 -7
- package/dist/src/backend/stdio-server.d.ts +4 -1
- package/dist/src/backend/stdio-server.js +240 -28
- package/dist/src/browser/active-composer-file-input.d.ts +9 -0
- package/dist/src/browser/active-composer-file-input.js +33 -0
- package/dist/src/browser/attach.d.ts +4 -1
- package/dist/src/browser/attach.js +136 -28
- package/dist/src/browser/downloads.d.ts +2 -0
- package/dist/src/client.d.ts +38 -0
- package/dist/src/client.js +1222 -59
- package/dist/src/commands/configuration.js +13 -7
- package/dist/src/commands/doctor.d.ts +1 -1
- package/dist/src/commands/doctor.js +26 -3
- package/dist/src/commands/experience.js +35 -0
- package/dist/src/commands/files.js +4 -27
- package/dist/src/commands/modes.js +49 -17
- package/dist/src/commands/power-discovery.d.ts +130 -0
- package/dist/src/commands/power-discovery.js +627 -0
- package/dist/src/commands/project-sources.js +325 -31
- package/dist/src/commands/sequence.js +59 -32
- package/dist/src/commands/session.js +7 -2
- package/dist/src/commands/work.js +31 -0
- package/dist/src/errors.d.ts +9 -0
- package/dist/src/errors.js +22 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +9 -0
- package/dist/src/operations/artifact-output.d.ts +69 -0
- package/dist/src/operations/artifact-output.js +1748 -0
- package/dist/src/operations/artifact-stream.d.ts +19 -0
- package/dist/src/operations/artifact-stream.js +35 -0
- package/dist/src/operations/artifact-transfer.d.ts +149 -0
- package/dist/src/operations/artifact-transfer.js +1312 -0
- package/dist/src/operations/browser-adapter.d.ts +161 -0
- package/dist/src/operations/browser-adapter.js +2023 -0
- package/dist/src/operations/browser-observation.d.ts +110 -0
- package/dist/src/operations/browser-observation.js +1208 -0
- package/dist/src/operations/browser-target.d.ts +103 -0
- package/dist/src/operations/browser-target.js +489 -0
- package/dist/src/operations/canonical.d.ts +4 -0
- package/dist/src/operations/canonical.js +412 -0
- package/dist/src/operations/chatgpt-runtime.d.ts +72 -0
- package/dist/src/operations/chatgpt-runtime.js +1150 -0
- package/dist/src/operations/client.d.ts +138 -0
- package/dist/src/operations/client.js +1143 -0
- package/dist/src/operations/collector.d.ts +195 -0
- package/dist/src/operations/collector.js +1039 -0
- package/dist/src/operations/control.d.ts +372 -0
- package/dist/src/operations/control.js +1498 -0
- package/dist/src/operations/file-identity.d.ts +33 -0
- package/dist/src/operations/file-identity.js +153 -0
- package/dist/src/operations/handle.d.ts +19 -0
- package/dist/src/operations/handle.js +727 -0
- package/dist/src/operations/index.d.ts +22 -0
- package/dist/src/operations/index.js +22 -0
- package/dist/src/operations/journal.d.ts +142 -0
- package/dist/src/operations/journal.js +1931 -0
- package/dist/src/operations/production-attachments.d.ts +116 -0
- package/dist/src/operations/production-attachments.js +1018 -0
- package/dist/src/operations/production-chatgpt-artifacts.d.ts +74 -0
- package/dist/src/operations/production-chatgpt-artifacts.js +1146 -0
- package/dist/src/operations/production-chatgpt-attachments.d.ts +71 -0
- package/dist/src/operations/production-chatgpt-attachments.js +1563 -0
- package/dist/src/operations/production-configuration.d.ts +42 -0
- package/dist/src/operations/production-configuration.js +1363 -0
- package/dist/src/operations/production-primitives.d.ts +53 -0
- package/dist/src/operations/production-primitives.js +1073 -0
- package/dist/src/operations/production-work-steer.d.ts +203 -0
- package/dist/src/operations/production-work-steer.js +1218 -0
- package/dist/src/operations/recovery.d.ts +80 -0
- package/dist/src/operations/recovery.js +174 -0
- package/dist/src/operations/runtime-adapter.d.ts +118 -0
- package/dist/src/operations/runtime-adapter.js +786 -0
- package/dist/src/operations/send-once.d.ts +224 -0
- package/dist/src/operations/send-once.js +1081 -0
- package/dist/src/operations/service.d.ts +281 -0
- package/dist/src/operations/service.js +2810 -0
- package/dist/src/operations/staging.d.ts +136 -0
- package/dist/src/operations/staging.js +630 -0
- package/dist/src/operations/state-machine.d.ts +29 -0
- package/dist/src/operations/state-machine.js +2047 -0
- package/dist/src/operations/submission.d.ts +423 -0
- package/dist/src/operations/submission.js +1676 -0
- package/dist/src/operations/turn-ownership.d.ts +202 -0
- package/dist/src/operations/turn-ownership.js +700 -0
- package/dist/src/operations/types.d.ts +454 -0
- package/dist/src/operations/types.js +20 -0
- package/dist/src/operations/wire-requests.d.ts +21 -0
- package/dist/src/operations/wire-requests.js +396 -0
- package/dist/src/operations/wire-results.d.ts +97 -0
- package/dist/src/operations/wire-results.js +818 -0
- package/dist/src/runner/responses.d.ts +3 -1
- package/dist/src/runner/responses.js +19 -1
- package/dist/src/runner/result.js +227 -55
- package/dist/src/runner/types.d.ts +12 -0
- package/dist/src/runtime/command-routing.d.ts +149 -0
- package/dist/src/runtime/command-routing.js +431 -0
- package/dist/src/runtime/coordinated-browser.d.ts +24 -0
- package/dist/src/runtime/coordinated-browser.js +315 -0
- package/dist/src/runtime/coordinated-page.d.ts +39 -0
- package/dist/src/runtime/coordinated-page.js +634 -0
- package/dist/src/runtime/operation-context.d.ts +142 -0
- package/dist/src/runtime/operation-context.js +410 -0
- package/dist/src/runtime/runtime-session.d.ts +95 -0
- package/dist/src/runtime/runtime-session.js +314 -0
- package/dist/src/runtime/tab-coordinator.d.ts +200 -0
- package/dist/src/runtime/tab-coordinator.js +1200 -0
- package/dist/src/runtime/value-boundaries.d.ts +18 -0
- package/dist/src/runtime/value-boundaries.js +49 -0
- package/dist/src/safety/untrusted-output.js +2 -1
- package/dist/src/scripts/backend-server.js +4 -1
- package/dist/src/scripts/live-smoke/harness.js +79 -7
- package/dist/src/scripts/live-smoke/scenarios.d.ts +28 -0
- package/dist/src/scripts/live-smoke/scenarios.js +110 -18
- package/dist/src/scripts/live-smoke/types.d.ts +3 -0
- package/dist/src/scripts/release-canary-module.js +17 -4
- package/dist/src/types.d.ts +38 -1
- package/package.json +1 -1
- package/references/2026-08-16-transactional-operations.md +459 -0
- package/references/agents-runner.md +34 -0
- package/references/backend-protocol.md +73 -0
- package/references/python-parity.md +72 -0
- package/references/responses-adapter.md +19 -0
- package/references/streaming.md +6 -0
|
@@ -3,6 +3,8 @@ import type { BootstrapArgs, ChatGPTExperience, ConfigurationSelection, Response
|
|
|
3
3
|
import type { ChatGPTAttachmentInput, ChatGPTInputItem, ChatGPTResponse, ChatGPTRunInput, ChatGPTRunResult, ChatGPTThreadSelector, ChatGPTVisibleModePreference, ChatGPTVisibleToolPreference, UnsupportedField } from "./types.js";
|
|
4
4
|
export type ChatGPTResponsesCreateArgs = {
|
|
5
5
|
input: string | ChatGPTInputItem[];
|
|
6
|
+
/** Caller-owned durable identity; opts this invocation into operations.run. */
|
|
7
|
+
operationId?: string;
|
|
6
8
|
thread?: ChatGPTThreadSelector;
|
|
7
9
|
existingTab?: BootstrapArgs["existingTab"];
|
|
8
10
|
preferExistingTab?: boolean;
|
|
@@ -29,4 +31,4 @@ export type ResponsesValidationResult = {
|
|
|
29
31
|
export declare function validateResponsesCreateArgs(args: Record<string, unknown>): ResponsesValidationResult;
|
|
30
32
|
export declare function responsesCreateArgsToRunInput(args: ChatGPTResponsesCreateArgs): ChatGPTRunInput;
|
|
31
33
|
export declare function responseFromRunResult<TOutput>(result: ChatGPTRunResult<TOutput>, now?: Date): ChatGPTResponse;
|
|
32
|
-
export declare function unsupportedResponse(unsupported: UnsupportedField[], now?: Date): ChatGPTResponse;
|
|
34
|
+
export declare function unsupportedResponse(unsupported: UnsupportedField[], now?: Date, operationId?: string): ChatGPTResponse;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { renderUntrustedOutputReturnEnvelope } from "../safety/untrusted-output.js";
|
|
2
2
|
const acceptedTopLevelFields = new Set([
|
|
3
3
|
"input",
|
|
4
|
+
"operationId",
|
|
4
5
|
"thread",
|
|
5
6
|
"existingTab",
|
|
6
7
|
"preferExistingTab",
|
|
@@ -38,6 +39,7 @@ const responseFormats = new Set([
|
|
|
38
39
|
"blocks",
|
|
39
40
|
"all"
|
|
40
41
|
]);
|
|
42
|
+
const operationIdPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
|
|
41
43
|
export function validateResponsesCreateArgs(args) {
|
|
42
44
|
const unsupported = [];
|
|
43
45
|
for (const [path, alternative] of Object.entries(unsupportedAlternatives)) {
|
|
@@ -61,6 +63,13 @@ export function validateResponsesCreateArgs(args) {
|
|
|
61
63
|
alternative: "Provide input: \"your visible prompt\"."
|
|
62
64
|
});
|
|
63
65
|
}
|
|
66
|
+
if (args.operationId !== undefined && (typeof args.operationId !== "string" || !operationIdPattern.test(args.operationId))) {
|
|
67
|
+
unsupported.push({
|
|
68
|
+
path: "operationId",
|
|
69
|
+
reason: "operationId must be a canonical UUID when provided.",
|
|
70
|
+
alternative: "Provide a caller-owned UUID, or omit operationId to retain the legacy runner path."
|
|
71
|
+
});
|
|
72
|
+
}
|
|
64
73
|
if (args.stream !== undefined && args.stream !== false) {
|
|
65
74
|
unsupported.push({
|
|
66
75
|
path: "stream",
|
|
@@ -108,6 +117,8 @@ export function responsesCreateArgsToRunInput(args) {
|
|
|
108
117
|
input: args.input,
|
|
109
118
|
response: { format: args.text?.format ?? "markdown" }
|
|
110
119
|
};
|
|
120
|
+
if (args.operationId !== undefined)
|
|
121
|
+
runInput.operationId = args.operationId;
|
|
111
122
|
if (args.thread !== undefined)
|
|
112
123
|
runInput.thread = args.thread;
|
|
113
124
|
if (args.existingTab !== undefined)
|
|
@@ -148,6 +159,12 @@ export function responseFromRunResult(result, now = new Date()) {
|
|
|
148
159
|
browserControl.completionState = completionState;
|
|
149
160
|
if (generationActive !== undefined)
|
|
150
161
|
browserControl.generationActive = generationActive;
|
|
162
|
+
const operationId = result.data?.operationId;
|
|
163
|
+
if (operationId !== undefined)
|
|
164
|
+
browserControl.operationId = operationId;
|
|
165
|
+
const handle = result.data?.handle;
|
|
166
|
+
if (handle !== undefined)
|
|
167
|
+
browserControl.handle = handle;
|
|
151
168
|
if (result.output_text.length > 0) {
|
|
152
169
|
const envelopeArgs = {
|
|
153
170
|
outputText: result.output_text,
|
|
@@ -173,7 +190,7 @@ export function responseFromRunResult(result, now = new Date()) {
|
|
|
173
190
|
browser_control: browserControl
|
|
174
191
|
};
|
|
175
192
|
}
|
|
176
|
-
export function unsupportedResponse(unsupported, now = new Date()) {
|
|
193
|
+
export function unsupportedResponse(unsupported, now = new Date(), operationId) {
|
|
177
194
|
return {
|
|
178
195
|
id: responseId(now),
|
|
179
196
|
object: "chatgpt.browser.response",
|
|
@@ -184,6 +201,7 @@ export function unsupportedResponse(unsupported, now = new Date()) {
|
|
|
184
201
|
browser_control: {
|
|
185
202
|
visibleUi: true,
|
|
186
203
|
resultStatus: "unsupported",
|
|
204
|
+
...(operationId === undefined ? {} : { operationId }),
|
|
187
205
|
unsupported
|
|
188
206
|
}
|
|
189
207
|
};
|
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
import { renderUntrustedOutputReturnEnvelope } from "../safety/untrusted-output.js";
|
|
2
2
|
import { interruptionFromCommandResult } from "./interruptions.js";
|
|
3
3
|
import { augmentCommandBlocker } from "./resume.js";
|
|
4
|
+
const MAX_RESULT_TRAVERSAL_DEPTH = 16;
|
|
5
|
+
const MAX_RESULT_TRAVERSAL_NODES = 2_048;
|
|
4
6
|
export function toRunResult(agent, result) {
|
|
5
|
-
const
|
|
7
|
+
const extractedOutput = extractOutput(result.data);
|
|
8
|
+
const outputText = extractedOutput?.text ?? "";
|
|
6
9
|
const finalOutput = parseFinalOutput(agent, outputText);
|
|
7
10
|
const interruption = interruptionFromCommandResult(result, failedCommand(result));
|
|
8
11
|
const interruptions = interruption === undefined ? [] : [interruption];
|
|
9
|
-
const output = runItemsFromResult(result, outputText);
|
|
12
|
+
const output = runItemsFromResult(result, outputText, extractedOutput?.source);
|
|
10
13
|
const state = runStateFromResult(result, interruptions);
|
|
11
14
|
const data = { outputText };
|
|
15
|
+
const operationId = readOperationId(result.data);
|
|
16
|
+
const handle = readOperationHandle(result.data);
|
|
17
|
+
const requestDigest = readRequestDigest(result.data);
|
|
18
|
+
if (operationId !== undefined)
|
|
19
|
+
data.operationId = operationId;
|
|
20
|
+
if (handle !== undefined)
|
|
21
|
+
data.handle = handle;
|
|
22
|
+
if (requestDigest !== undefined)
|
|
23
|
+
data.requestDigest = requestDigest;
|
|
12
24
|
const submissionState = readSubmissionState(result.data);
|
|
13
25
|
const completionState = readCompletionState(result.data);
|
|
14
26
|
const generationActive = readGenerationActive(result.data);
|
|
@@ -54,19 +66,25 @@ export function toRunResult(agent, result) {
|
|
|
54
66
|
mapped.finalOutput = finalOutput;
|
|
55
67
|
return mapped;
|
|
56
68
|
}
|
|
57
|
-
function
|
|
58
|
-
if (!
|
|
59
|
-
return
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
function extractOutput(data, seen = new WeakSet(), depth = 0, budget = { remaining: MAX_RESULT_TRAVERSAL_NODES }) {
|
|
70
|
+
if (!enterResultRecord(data, seen, depth, budget))
|
|
71
|
+
return undefined;
|
|
72
|
+
const responseText = ownDataProperty(data, "responseText");
|
|
73
|
+
if (typeof responseText === "string") {
|
|
74
|
+
return { text: responseText, source: data };
|
|
75
|
+
}
|
|
76
|
+
const text = ownDataProperty(data, "text");
|
|
77
|
+
if (typeof text === "string") {
|
|
78
|
+
return { text, source: data };
|
|
79
|
+
}
|
|
80
|
+
for (const value of ownDataValues(data)) {
|
|
81
|
+
const nested = extractOutput(value, seen, depth + 1, budget);
|
|
82
|
+
// Keep the pre-existing extractor's behavior: an empty nested record is
|
|
83
|
+
// not a usable result, so continue searching siblings for real output.
|
|
84
|
+
if (nested !== undefined && nested.text.length > 0)
|
|
67
85
|
return nested;
|
|
68
86
|
}
|
|
69
|
-
return
|
|
87
|
+
return undefined;
|
|
70
88
|
}
|
|
71
89
|
function parseFinalOutput(agent, outputText) {
|
|
72
90
|
if (outputText.length === 0)
|
|
@@ -81,16 +99,16 @@ function parseFinalOutput(agent, outputText) {
|
|
|
81
99
|
}
|
|
82
100
|
return outputText;
|
|
83
101
|
}
|
|
84
|
-
function runItemsFromResult(result, outputText) {
|
|
102
|
+
function runItemsFromResult(result, outputText, outputSource) {
|
|
103
|
+
const responseFormat = responseFormatForOutput(result.data, outputSource);
|
|
85
104
|
const items = lifecycleItemsFromSteps(result.steps);
|
|
105
|
+
// Prompt provenance is independent from assistant-output provenance: a
|
|
106
|
+
// submitted prompt may be recorded in a sibling branch of the result.
|
|
86
107
|
items.push(...messageItemsFromData(result.data));
|
|
87
108
|
if (!items.some(item => item.type === "message.completed" || item.type === "message.in_progress") && outputText.length > 0) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
else {
|
|
92
|
-
items.push({ type: "message.completed", role: "assistant", output_text: outputText, format: "markdown" });
|
|
93
|
-
}
|
|
109
|
+
const assistant = assistantItemFromOutput(outputSource, outputText, responseFormat, result.status);
|
|
110
|
+
if (assistant !== undefined)
|
|
111
|
+
items.push(assistant);
|
|
94
112
|
}
|
|
95
113
|
if (result.blocker !== undefined) {
|
|
96
114
|
items.push({ type: "run.blocked", blocker: augmentCommandBlocker(result.blocker) });
|
|
@@ -132,34 +150,55 @@ function lifecycleItemsFromSteps(steps) {
|
|
|
132
150
|
}
|
|
133
151
|
return items;
|
|
134
152
|
}
|
|
135
|
-
function messageItemsFromData(data) {
|
|
136
|
-
if (!
|
|
153
|
+
function messageItemsFromData(data, seen = new WeakSet(), depth = 0, budget = { remaining: MAX_RESULT_TRAVERSAL_NODES }) {
|
|
154
|
+
if (!enterResultRecord(data, seen, depth, budget))
|
|
137
155
|
return [];
|
|
138
156
|
const items = [];
|
|
139
|
-
|
|
157
|
+
const prompt = ownDataProperty(data, "prompt");
|
|
158
|
+
if (typeof prompt === "string" && prompt.length > 0) {
|
|
140
159
|
items.push({
|
|
141
160
|
type: "message.submitted",
|
|
142
161
|
role: "user",
|
|
143
|
-
preview:
|
|
162
|
+
preview: prompt.length > 160 ? `${prompt.slice(0, 159)}...` : prompt,
|
|
144
163
|
redacted: true
|
|
145
164
|
});
|
|
146
|
-
}
|
|
147
|
-
if (typeof data.responseText === "string" && data.responseText.length > 0) {
|
|
148
|
-
if (readCompletionState(data) === "complete" || data.complete === true) {
|
|
149
|
-
items.push({ type: "message.completed", role: "assistant", output_text: data.responseText, format: "markdown" });
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
items.push(inProgressItem(data.responseText, readCompletionState(data), readGenerationActive(data)));
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
if (items.length > 0)
|
|
156
165
|
return items;
|
|
157
|
-
|
|
158
|
-
|
|
166
|
+
}
|
|
167
|
+
for (const value of ownDataValues(data)) {
|
|
168
|
+
const nested = messageItemsFromData(value, seen, depth + 1, budget);
|
|
159
169
|
if (nested.length > 0)
|
|
160
170
|
return nested;
|
|
161
171
|
}
|
|
162
|
-
return
|
|
172
|
+
return items;
|
|
173
|
+
}
|
|
174
|
+
function assistantItemFromOutput(outputSource, outputText, responseFormat, resultStatus) {
|
|
175
|
+
if (outputSource === undefined || outputText.length === 0)
|
|
176
|
+
return undefined;
|
|
177
|
+
// The selected output record is the sole authority for assistant text and
|
|
178
|
+
// lifecycle metadata. In particular, do not recursively borrow completion
|
|
179
|
+
// state from a later sibling branch.
|
|
180
|
+
const completionState = completionStateFromRecord(outputSource);
|
|
181
|
+
const complete = ownDataProperty(outputSource, "complete");
|
|
182
|
+
if (completionState === "complete" || complete === true) {
|
|
183
|
+
return { type: "message.completed", role: "assistant", output_text: outputText, format: responseFormat };
|
|
184
|
+
}
|
|
185
|
+
const incomplete = complete === false
|
|
186
|
+
|| completionState !== undefined
|
|
187
|
+
|| resultStatus === "partial";
|
|
188
|
+
if (incomplete) {
|
|
189
|
+
return inProgressItem(outputText, completionState, generationActiveFromRecord(outputSource), responseFormat);
|
|
190
|
+
}
|
|
191
|
+
return { type: "message.completed", role: "assistant", output_text: outputText, format: responseFormat };
|
|
192
|
+
}
|
|
193
|
+
function completionStateFromRecord(data) {
|
|
194
|
+
const value = ownDataProperty(data, "completionState");
|
|
195
|
+
return value === "complete" || value === "generating" || value === "stopped" || value === "partial" || value === "unknown"
|
|
196
|
+
? value
|
|
197
|
+
: undefined;
|
|
198
|
+
}
|
|
199
|
+
function generationActiveFromRecord(data) {
|
|
200
|
+
const value = ownDataProperty(data, "generationActive");
|
|
201
|
+
return typeof value === "boolean" ? value : undefined;
|
|
163
202
|
}
|
|
164
203
|
function runStateFromResult(result, interruptions) {
|
|
165
204
|
const resumable = interruptions.some(interruption => interruption.resume.supported);
|
|
@@ -168,6 +207,17 @@ function runStateFromResult(result, interruptions) {
|
|
|
168
207
|
id: firstResume?.supported === true && firstResume.stateId !== undefined ? firstResume.stateId : `run_${Date.now().toString(36)}`,
|
|
169
208
|
resumable
|
|
170
209
|
};
|
|
210
|
+
const operationId = readOperationId(result.data);
|
|
211
|
+
const handle = readOperationHandle(result.data);
|
|
212
|
+
if (operationId !== undefined) {
|
|
213
|
+
state.operationId = operationId;
|
|
214
|
+
// Use the durable identity for correlation without changing the runner's
|
|
215
|
+
// resume-policy bit: recovery is collect-only through the operation
|
|
216
|
+
// handle, not permission to replay the high-level workflow.
|
|
217
|
+
state.id = operationId;
|
|
218
|
+
}
|
|
219
|
+
if (handle !== undefined)
|
|
220
|
+
state.handle = handle;
|
|
171
221
|
const thread = threadRefFromContext(result.context);
|
|
172
222
|
if (thread !== undefined)
|
|
173
223
|
state.thread = thread;
|
|
@@ -179,13 +229,13 @@ function runStateFromResult(result, interruptions) {
|
|
|
179
229
|
state.completionState = completionState;
|
|
180
230
|
return state;
|
|
181
231
|
}
|
|
182
|
-
function inProgressItem(outputText, completionState, generationActive) {
|
|
232
|
+
function inProgressItem(outputText, completionState, generationActive, responseFormat) {
|
|
183
233
|
const item = {
|
|
184
234
|
type: "message.in_progress",
|
|
185
235
|
role: "assistant",
|
|
186
236
|
output_text: outputText,
|
|
187
237
|
preview: outputText.length > 160 ? `${outputText.slice(0, 159)}...` : outputText,
|
|
188
|
-
format:
|
|
238
|
+
format: responseFormat,
|
|
189
239
|
textLength: outputText.length,
|
|
190
240
|
textHash: hashText(outputText)
|
|
191
241
|
};
|
|
@@ -195,46 +245,168 @@ function inProgressItem(outputText, completionState, generationActive) {
|
|
|
195
245
|
item.generationActive = generationActive;
|
|
196
246
|
return item;
|
|
197
247
|
}
|
|
198
|
-
function
|
|
199
|
-
|
|
248
|
+
function responseFormatForOutput(data, outputSource) {
|
|
249
|
+
// A top-level responseFormat is an explicit result contract and therefore
|
|
250
|
+
// remains authoritative even when the output text is nested below it.
|
|
251
|
+
const topLevel = ownDataProperty(data, "responseFormat");
|
|
252
|
+
if (isResponseFormat(topLevel))
|
|
253
|
+
return topLevel;
|
|
254
|
+
// Legacy format fields are only meaningful when co-located with the record
|
|
255
|
+
// whose text won the bounded output traversal. Never borrow one from an
|
|
256
|
+
// unrelated metadata/configuration branch.
|
|
257
|
+
const local = responseFormatFromRecord(outputSource);
|
|
258
|
+
return local ?? "markdown";
|
|
259
|
+
}
|
|
260
|
+
function responseFormatFromRecord(data) {
|
|
261
|
+
if (data === undefined)
|
|
200
262
|
return undefined;
|
|
201
|
-
const
|
|
263
|
+
const explicit = ownDataProperty(data, "responseFormat");
|
|
264
|
+
if (isResponseFormat(explicit))
|
|
265
|
+
return explicit;
|
|
266
|
+
const local = ownDataProperty(data, "format");
|
|
267
|
+
return isResponseFormat(local) ? local : undefined;
|
|
268
|
+
}
|
|
269
|
+
function isResponseFormat(value) {
|
|
270
|
+
return value === "markdown"
|
|
271
|
+
|| value === "text"
|
|
272
|
+
|| value === "normalized_text"
|
|
273
|
+
|| value === "visible_text"
|
|
274
|
+
|| value === "html"
|
|
275
|
+
|| value === "blocks"
|
|
276
|
+
|| value === "all";
|
|
277
|
+
}
|
|
278
|
+
function readCompletionState(data, seen = new WeakSet(), depth = 0, budget = { remaining: MAX_RESULT_TRAVERSAL_NODES }) {
|
|
279
|
+
if (!enterResultRecord(data, seen, depth, budget))
|
|
280
|
+
return undefined;
|
|
281
|
+
const value = ownDataProperty(data, "completionState");
|
|
202
282
|
if (value === "complete" || value === "generating" || value === "stopped" || value === "partial" || value === "unknown") {
|
|
203
283
|
return value;
|
|
204
284
|
}
|
|
205
|
-
for (const nested of
|
|
206
|
-
const nestedState = readCompletionState(nested);
|
|
285
|
+
for (const nested of ownDataValues(data)) {
|
|
286
|
+
const nestedState = readCompletionState(nested, seen, depth + 1, budget);
|
|
207
287
|
if (nestedState !== undefined)
|
|
208
288
|
return nestedState;
|
|
209
289
|
}
|
|
210
290
|
return undefined;
|
|
211
291
|
}
|
|
212
|
-
function readSubmissionState(data) {
|
|
213
|
-
if (!
|
|
292
|
+
function readSubmissionState(data, seen = new WeakSet(), depth = 0, budget = { remaining: MAX_RESULT_TRAVERSAL_NODES }) {
|
|
293
|
+
if (!enterResultRecord(data, seen, depth, budget))
|
|
214
294
|
return undefined;
|
|
215
|
-
const value = data
|
|
295
|
+
const value = ownDataProperty(data, "submissionState");
|
|
216
296
|
if (value === "not_submitted" || value === "submitted" || value === "submitted_unconfirmed" || value === "submitted_generating") {
|
|
217
297
|
return value;
|
|
218
298
|
}
|
|
219
|
-
for (const nested of
|
|
220
|
-
const nestedState = readSubmissionState(nested);
|
|
299
|
+
for (const nested of ownDataValues(data)) {
|
|
300
|
+
const nestedState = readSubmissionState(nested, seen, depth + 1, budget);
|
|
221
301
|
if (nestedState !== undefined)
|
|
222
302
|
return nestedState;
|
|
223
303
|
}
|
|
224
304
|
return undefined;
|
|
225
305
|
}
|
|
226
|
-
function readGenerationActive(data) {
|
|
227
|
-
if (!
|
|
306
|
+
function readGenerationActive(data, seen = new WeakSet(), depth = 0, budget = { remaining: MAX_RESULT_TRAVERSAL_NODES }) {
|
|
307
|
+
if (!enterResultRecord(data, seen, depth, budget))
|
|
228
308
|
return undefined;
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
309
|
+
const generationActive = ownDataProperty(data, "generationActive");
|
|
310
|
+
if (typeof generationActive === "boolean")
|
|
311
|
+
return generationActive;
|
|
312
|
+
for (const nested of ownDataValues(data)) {
|
|
313
|
+
const value = readGenerationActive(nested, seen, depth + 1, budget);
|
|
233
314
|
if (value !== undefined)
|
|
234
315
|
return value;
|
|
235
316
|
}
|
|
236
317
|
return undefined;
|
|
237
318
|
}
|
|
319
|
+
function readOperationId(data) {
|
|
320
|
+
const value = ownDataProperty(data, "operationId");
|
|
321
|
+
return typeof value === "string"
|
|
322
|
+
&& /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(value)
|
|
323
|
+
? value
|
|
324
|
+
: undefined;
|
|
325
|
+
}
|
|
326
|
+
function readRequestDigest(data) {
|
|
327
|
+
const value = ownDataProperty(data, "requestDigest");
|
|
328
|
+
return typeof value === "string" && /^hmac-sha256:[0-9a-f]{64}$/u.test(value)
|
|
329
|
+
? value
|
|
330
|
+
: undefined;
|
|
331
|
+
}
|
|
332
|
+
function readOperationHandle(data) {
|
|
333
|
+
const value = ownDataProperty(data, "handle");
|
|
334
|
+
if (!isRecord(value))
|
|
335
|
+
return undefined;
|
|
336
|
+
const operationId = ownDataProperty(value, "operationId");
|
|
337
|
+
const requestDigest = ownDataProperty(value, "requestDigest");
|
|
338
|
+
const schemaVersion = ownDataProperty(value, "schemaVersion");
|
|
339
|
+
const surface = ownDataProperty(value, "surface");
|
|
340
|
+
const revision = ownDataProperty(value, "revision");
|
|
341
|
+
const phase = ownDataProperty(value, "phase");
|
|
342
|
+
const mutationBoundary = ownDataProperty(value, "mutationBoundary");
|
|
343
|
+
const targetBindingDigest = ownDataProperty(value, "targetBindingDigest");
|
|
344
|
+
if (schemaVersion !== "chatgpt.browser_control.operation_handle.v1"
|
|
345
|
+
|| typeof operationId !== "string"
|
|
346
|
+
|| !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(operationId)
|
|
347
|
+
|| typeof requestDigest !== "string"
|
|
348
|
+
|| !/^hmac-sha256:[0-9a-f]{64}$/u.test(requestDigest)
|
|
349
|
+
|| (surface !== "chat" && surface !== "work")
|
|
350
|
+
|| !Number.isSafeInteger(revision)
|
|
351
|
+
|| revision < 1
|
|
352
|
+
|| !["prepared", "handoff_pending", "ready", "send_pending", "submitted", "generating", "capturing", "completed", "uncertain"].includes(String(phase))
|
|
353
|
+
|| !["none", "handoff_may_have_occurred", "send_may_have_occurred", "control_may_have_occurred"].includes(String(mutationBoundary))
|
|
354
|
+
|| (targetBindingDigest !== undefined
|
|
355
|
+
&& (typeof targetBindingDigest !== "string" || !/^hmac-sha256:[0-9a-f]{64}$/u.test(targetBindingDigest)))) {
|
|
356
|
+
return undefined;
|
|
357
|
+
}
|
|
358
|
+
return Object.freeze({
|
|
359
|
+
schemaVersion,
|
|
360
|
+
operationId,
|
|
361
|
+
requestDigest,
|
|
362
|
+
surface,
|
|
363
|
+
revision: revision,
|
|
364
|
+
phase: phase,
|
|
365
|
+
mutationBoundary: mutationBoundary,
|
|
366
|
+
...(targetBindingDigest === undefined ? {} : { targetBindingDigest })
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
/** Never invoke a getter while extracting operation metadata from result data. */
|
|
370
|
+
function ownDataProperty(value, key) {
|
|
371
|
+
if (!isRecord(value))
|
|
372
|
+
return undefined;
|
|
373
|
+
try {
|
|
374
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
375
|
+
return descriptor !== undefined && "value" in descriptor ? descriptor.value : undefined;
|
|
376
|
+
}
|
|
377
|
+
catch {
|
|
378
|
+
return undefined;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/** Enumerate data values without invoking getters or trusting proxy traps. */
|
|
382
|
+
function ownDataValues(value) {
|
|
383
|
+
if (!isRecord(value))
|
|
384
|
+
return [];
|
|
385
|
+
try {
|
|
386
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
387
|
+
const values = [];
|
|
388
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
389
|
+
if (typeof key !== "string")
|
|
390
|
+
continue;
|
|
391
|
+
const descriptor = descriptors[key];
|
|
392
|
+
if (descriptor !== undefined && "value" in descriptor)
|
|
393
|
+
values.push(descriptor.value);
|
|
394
|
+
}
|
|
395
|
+
return values;
|
|
396
|
+
}
|
|
397
|
+
catch {
|
|
398
|
+
return [];
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function enterResultRecord(value, seen, depth, budget) {
|
|
402
|
+
if (!isRecord(value) || depth > MAX_RESULT_TRAVERSAL_DEPTH || budget.remaining <= 0)
|
|
403
|
+
return false;
|
|
404
|
+
if (seen.has(value))
|
|
405
|
+
return false;
|
|
406
|
+
seen.add(value);
|
|
407
|
+
budget.remaining -= 1;
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
238
410
|
function hashText(value) {
|
|
239
411
|
let hash = 2166136261;
|
|
240
412
|
for (let index = 0; index < value.length; index += 1) {
|
|
@@ -2,6 +2,7 @@ import type { AttachedFile, ChatGPTExperience, CommandResult, CommandStatus, Com
|
|
|
2
2
|
import type { RunReportOptions } from "../commands/reports.js";
|
|
3
3
|
import type { UntrustedOutputReturnEnvelope } from "../safety/untrusted-output.js";
|
|
4
4
|
import type { ChatGPTRunStream } from "./stream.js";
|
|
5
|
+
import type { OperationHandleV1 } from "../operations/types.js";
|
|
5
6
|
export type ChatGPTThreadSelector = {
|
|
6
7
|
type: "new";
|
|
7
8
|
} | {
|
|
@@ -106,6 +107,8 @@ export type ChatGPTInputItem = {
|
|
|
106
107
|
};
|
|
107
108
|
export type ChatGPTRunInput = string | {
|
|
108
109
|
input: string | ChatGPTInputItem[];
|
|
110
|
+
/** Caller-owned durable identity; opts this invocation into operations.run. */
|
|
111
|
+
operationId?: string;
|
|
109
112
|
thread?: ChatGPTThreadSelector;
|
|
110
113
|
existingTab?: BootstrapArgs["existingTab"];
|
|
111
114
|
preferExistingTab?: boolean;
|
|
@@ -240,6 +243,8 @@ export type ChatGPTConfirmation = {
|
|
|
240
243
|
export type ChatGPTRunState = {
|
|
241
244
|
id: string;
|
|
242
245
|
resumable: boolean;
|
|
246
|
+
operationId?: string;
|
|
247
|
+
handle?: OperationHandleV1;
|
|
243
248
|
thread?: ChatGPTThreadRef;
|
|
244
249
|
nextStepId?: string;
|
|
245
250
|
submissionState?: SubmissionState;
|
|
@@ -248,6 +253,11 @@ export type ChatGPTRunState = {
|
|
|
248
253
|
export type ChatGPTRunData<TOutput = string> = {
|
|
249
254
|
finalOutput?: TOutput;
|
|
250
255
|
outputText: string;
|
|
256
|
+
/** Effective caller-owned operation identity when the transactional path was selected. */
|
|
257
|
+
operationId?: string;
|
|
258
|
+
/** Durable locator returned by the transactional operation path. */
|
|
259
|
+
handle?: OperationHandleV1;
|
|
260
|
+
requestDigest?: string;
|
|
251
261
|
untrustedOutput?: UntrustedOutputReturnEnvelope;
|
|
252
262
|
thread?: ChatGPTThreadRef;
|
|
253
263
|
downloads?: DownloadedFileSummary[];
|
|
@@ -290,6 +300,8 @@ export type ChatGPTResponse = {
|
|
|
290
300
|
submissionState?: SubmissionState;
|
|
291
301
|
completionState?: CompletionState;
|
|
292
302
|
generationActive?: boolean;
|
|
303
|
+
operationId?: string;
|
|
304
|
+
handle?: OperationHandleV1;
|
|
293
305
|
untrustedOutput?: UntrustedOutputReturnEnvelope;
|
|
294
306
|
unsupported?: UnsupportedField[];
|
|
295
307
|
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { ProcessTabCoordinator, type CoordinatorAcquisitionContext, type CoordinatorPriority } from "./tab-coordinator.js";
|
|
2
|
+
import type { OperationRuntimeContext } from "./operation-context.js";
|
|
3
|
+
import { type CoordinatedBrowserOptions } from "./coordinated-browser.js";
|
|
4
|
+
import type { RuntimeEnv } from "../types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Routing keeps the implementation classes visible while also exposing a
|
|
7
|
+
* binary browser-free/coordinator-routed disposition for the acceptance
|
|
8
|
+
* contract. Legacy command implementations still run directly, but their
|
|
9
|
+
* page and browser method calls use the bounded coordinated runtime facade.
|
|
10
|
+
* That does not make the whole command or its polling loop one coordinator
|
|
11
|
+
* transaction.
|
|
12
|
+
*
|
|
13
|
+
* `operation_opt_in` means that an explicit caller-owned operation identity is
|
|
14
|
+
* handled by an operation-aware public facade. It does not authorize wrapping
|
|
15
|
+
* the whole public command in a coordinator callback.
|
|
16
|
+
*/
|
|
17
|
+
export type CommandRoutingClass = "browser_free" | "operation_opt_in" | "legacy_page_facade" | "legacy_browser_unrouted" | "coordinator_entrypoint";
|
|
18
|
+
/** The complete routing inventory is intentionally explicit and reviewable. */
|
|
19
|
+
export declare const COMMAND_ROUTING_INVENTORY: Readonly<{
|
|
20
|
+
browserFree: readonly ["backend.version", "backend.health", "backend.capabilities", "backend.hello", "runner.plan", "createReport", "reports.create", "reports.redact", "reports.summarize", "commands", "describe", "help", "redacted-run-report", "files.preflight", "projects.sources.planAdd", "operations.inspect"];
|
|
21
|
+
/**
|
|
22
|
+
* These commands opt into the operation facade only when an operation
|
|
23
|
+
* identity is present. With no identity they retain their legacy path.
|
|
24
|
+
*/
|
|
25
|
+
operationOptIn: readonly ["runner.run", "runner.stream", "responses.create", "ask", "askInThread", "askWithFiles", "askAndDownload", "work.start", "work.steer", "operations.submit", "operations.collect", "operations.control"];
|
|
26
|
+
/**
|
|
27
|
+
* Legacy commands retain their public workflow and now receive a
|
|
28
|
+
* coordinator-backed PageLike/BrowserLike facade. This is a method-level
|
|
29
|
+
* guarantee: it does not turn the whole command or its polling loop into a
|
|
30
|
+
* single actor callback.
|
|
31
|
+
*/
|
|
32
|
+
legacyPageFacade: readonly ["runMessages", "openThread", "copyLatest", "readLatest", "downloadLatest", "runPlan", "new-ask-read", "find-open-ask-read", "find-open-copy-latest", "attach-ask-read", "ask-and-download", "two-turn", "doctor-upload", "doctor", "session.bootstrap", "experience.detect", "experience.open", "configuration.inspect", "configuration.apply", "work.status", "work.wait", "work.readLatest", "threads.new", "threads.search", "threads.open", "messages.compose", "messages.submit", "messages.ask", "messages.wait", "messages.readLatest", "messages.status", "messages.stop", "messages.waitAndRead", "artifacts.listLatest", "artifacts.wait", "artifacts.downloadLatest", "files.attach", "files.downloadLatest", "projects.sources.list", "projects.sources.add", "response.copy", "modes.set", "modes.get", "tools.select"];
|
|
33
|
+
/**
|
|
34
|
+
* Browser acquisition seams not covered by the facade remain explicit
|
|
35
|
+
* migration gaps. The current command surface has none; discovery is
|
|
36
|
+
* coordinated in browser/attach.ts.
|
|
37
|
+
*/
|
|
38
|
+
legacyBrowserUnrouted: readonly [];
|
|
39
|
+
/**
|
|
40
|
+
* No backend or sequence command currently invokes
|
|
41
|
+
* routeCommandBrowserTransaction. Keep this category explicit so a future
|
|
42
|
+
* migration cannot silently turn a classification into an enforcement
|
|
43
|
+
* claim. A command may enter this list only after its own bounded DOM seam
|
|
44
|
+
* is implemented and tested.
|
|
45
|
+
*/
|
|
46
|
+
coordinatorEntrypoint: readonly [];
|
|
47
|
+
}>;
|
|
48
|
+
type InventoryCommand = typeof COMMAND_ROUTING_INVENTORY.browserFree[number] | typeof COMMAND_ROUTING_INVENTORY.operationOptIn[number] | typeof COMMAND_ROUTING_INVENTORY.legacyPageFacade[number] | typeof COMMAND_ROUTING_INVENTORY.legacyBrowserUnrouted[number] | typeof COMMAND_ROUTING_INVENTORY.coordinatorEntrypoint[number];
|
|
49
|
+
type LegacyBrowserCommand = typeof COMMAND_ROUTING_INVENTORY.legacyBrowserUnrouted[number];
|
|
50
|
+
/**
|
|
51
|
+
* The operation-aware public dispatch seams currently implemented by the
|
|
52
|
+
* client/backend path. This is separate from the coordinator entrypoint
|
|
53
|
+
* inventory: these commands route into the operation facade, whose adapter
|
|
54
|
+
* owns its own short tab transactions, rather than calling the generic helper
|
|
55
|
+
* below around the whole command.
|
|
56
|
+
*/
|
|
57
|
+
export declare const OPERATION_AWARE_DISPATCH_COMMANDS: readonly ["runner.run", "runner.stream", "responses.create", "ask", "askInThread", "askWithFiles", "askAndDownload", "work.start", "work.steer", "operations.submit", "operations.collect", "operations.control"];
|
|
58
|
+
export type CommandRoutingGap = Readonly<{
|
|
59
|
+
command: LegacyBrowserCommand;
|
|
60
|
+
status: "legacy_browser_unrouted";
|
|
61
|
+
owner: string;
|
|
62
|
+
requiredSeam: "bounded_tab_transaction";
|
|
63
|
+
reason: "legacy_command_dispatch_has_no_operation_aware_tab_seam";
|
|
64
|
+
}>;
|
|
65
|
+
export declare const COMMAND_ROUTING_GAPS: readonly CommandRoutingGap[];
|
|
66
|
+
/** Return undefined for a command that has not been explicitly classified. */
|
|
67
|
+
export declare function classifyCommandRouting(command: string): CommandRoutingClass | undefined;
|
|
68
|
+
export declare function isBrowserFreeCommand(command: string): boolean;
|
|
69
|
+
export declare function isOperationOptInCommand(command: string): boolean;
|
|
70
|
+
export declare function isLegacyBrowserUnroutedCommand(command: string): boolean;
|
|
71
|
+
export declare function isLegacyPageFacadeCommand(command: string): boolean;
|
|
72
|
+
export declare function isCoordinatorEntrypointCommand(command: string): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* The public acceptance contract intentionally collapses the implementation
|
|
75
|
+
* classes into the two meaningful routing dispositions. `operation_opt_in`
|
|
76
|
+
* is coordinator-routed by its operation adapter; the legacy page facade is
|
|
77
|
+
* coordinator-routed by the bounded environment adapter below. An explicit
|
|
78
|
+
* `legacy_browser_unrouted` entry is a migration gap and therefore has no
|
|
79
|
+
* safe disposition.
|
|
80
|
+
*/
|
|
81
|
+
export type CommandRoutingDisposition = "browser_free" | "coordinator_routed";
|
|
82
|
+
export declare function commandRoutingDisposition(command: string): CommandRoutingDisposition | undefined;
|
|
83
|
+
export declare function isCoordinatorRoutedCommand(command: string): boolean;
|
|
84
|
+
/** Options for the bounded legacy command adapter. */
|
|
85
|
+
export type CommandRoutingAdapterOptions = Readonly<Pick<CoordinatedBrowserOptions, "coordinator" | "owner">>;
|
|
86
|
+
/**
|
|
87
|
+
* Prepare the environment for one command invocation.
|
|
88
|
+
*
|
|
89
|
+
* This is deliberately an adapter rather than a whole-command coordinator
|
|
90
|
+
* transaction. `coordinateRuntimeEnv` returns PageLike/BrowserLike facades
|
|
91
|
+
* whose individual browser calls acquire the process-scoped actor for a
|
|
92
|
+
* bounded operation and release it before the next await. Consequently a
|
|
93
|
+
* command may wait for generation, poll, write a journal, or invoke caller
|
|
94
|
+
* code without retaining a tab actor. The coordinated browser/page values
|
|
95
|
+
* are copied back into the supplied invocation environment so legacy command
|
|
96
|
+
* mutations (notably `session.bootstrap`) remain visible to later sequence
|
|
97
|
+
* steps. Calling this on an already-coordinated environment is idempotent
|
|
98
|
+
* through the wrapper caches.
|
|
99
|
+
*/
|
|
100
|
+
export declare function routeCommandRuntimeEnv(command: string, env: RuntimeEnv, options?: CommandRoutingAdapterOptions): RuntimeEnv;
|
|
101
|
+
/**
|
|
102
|
+
* Run one command with the bounded legacy environment adapter.
|
|
103
|
+
*
|
|
104
|
+
* The callback is invoked outside any coordinator callback. The returned
|
|
105
|
+
* PageLike/BrowserLike values enforce short method-level transactions, so a
|
|
106
|
+
* caller callback or a generation/poll sleep cannot deadlock a nested actor.
|
|
107
|
+
*/
|
|
108
|
+
export declare function routeCommandExecution<T>(command: string, env: RuntimeEnv, callback: (routedEnv: RuntimeEnv) => T | PromiseLike<T>, options?: CommandRoutingAdapterOptions): Promise<T>;
|
|
109
|
+
export type CommandRoutingContext<Page extends object = object> = Readonly<{
|
|
110
|
+
coordinator: ProcessTabCoordinator;
|
|
111
|
+
runtimeContext: OperationRuntimeContext<Page>;
|
|
112
|
+
priority?: CoordinatorPriority;
|
|
113
|
+
signal?: AbortSignal;
|
|
114
|
+
deadlineAt?: number;
|
|
115
|
+
timeoutMs?: number;
|
|
116
|
+
label?: string;
|
|
117
|
+
}>;
|
|
118
|
+
export type CommandRoutingErrorCode = "unclassified_command" | "coordinator_context_required" | "exact_ownership_unavailable" | "legacy_command_unrouted" | "legacy_page_facade" | "operation_facade_managed" | "operation_routing_unavailable";
|
|
119
|
+
/** Stable error for callers that try to route without enough ownership proof. */
|
|
120
|
+
export declare class CommandRoutingError extends Error {
|
|
121
|
+
readonly code: CommandRoutingErrorCode;
|
|
122
|
+
constructor(code: CommandRoutingErrorCode);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Execute one already-bounded browser transaction for an explicit coordinator
|
|
126
|
+
* entrypoint. The callback is intentionally the transaction boundary, not
|
|
127
|
+
* the command or request boundary. Callers must keep generation waits,
|
|
128
|
+
* polling sleeps, file hashing/transfers, journal writes, and report/caller
|
|
129
|
+
* callbacks outside this helper.
|
|
130
|
+
*/
|
|
131
|
+
export declare function routeCommandBrowserTransaction<T, Page extends object = object>(command: string, context: CommandRoutingContext<Page> | undefined, callback: (acquisition: CoordinatorAcquisitionContext | undefined) => T | PromiseLike<T>): Promise<T>;
|
|
132
|
+
/** Return a readonly snapshot useful for diagnostics and inventory tests. */
|
|
133
|
+
export declare function commandRoutingInventory(): Readonly<Record<string, CommandRoutingClass>>;
|
|
134
|
+
/**
|
|
135
|
+
* Return true when a request carries one of the reserved caller-owned
|
|
136
|
+
* operation identity fields. This is intentionally structural and never
|
|
137
|
+
* reads or serializes prompt/instruction values.
|
|
138
|
+
*/
|
|
139
|
+
export declare function hasOperationIdentity(value: unknown): boolean;
|
|
140
|
+
/**
|
|
141
|
+
* Backend and sequence dispatchers call this before direct command dispatch.
|
|
142
|
+
* Legacy callers without an operation identity remain byte-for-byte on the
|
|
143
|
+
* existing path. An operation-aware request is allowed only for explicitly
|
|
144
|
+
* migrated facade commands (or browser-free diagnostics); all other browser
|
|
145
|
+
* commands fail before their handler can touch the browser.
|
|
146
|
+
*/
|
|
147
|
+
export declare function assertOperationAwareDispatchAllowed(command: string, payload: unknown): void;
|
|
148
|
+
export type ClassifiedCommand = InventoryCommand;
|
|
149
|
+
export {};
|