codex-chatgpt-control 0.5.1-alpha.2 → 0.5.1-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/README.md +43 -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/download-blocked-by-browser.json +22 -0
- package/contracts/v1/fixtures/download-receipt-failed.json +22 -0
- package/contracts/v1/fixtures/download-receipt-timeout.json +22 -0
- package/contracts/v1/fixtures/journal-rpc-indeterminate.json +32 -0
- package/contracts/v1/fixtures/journal-rpc-unsupported-platform.json +32 -0
- package/contracts/v1/fixtures/journal-runtime-unavailable.json +32 -0
- 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 +185 -1
- package/contracts/v1/parity-suite.json +106 -8
- 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 +45882 -9361
- package/dist/codex-chatgpt-control-journal.mjs +5547 -0
- package/dist/codex-chatgpt-control-live-smoke.bundle.mjs +51677 -0
- package/dist/codex-chatgpt-control-release-canary.bundle.mjs +52104 -0
- package/dist/codex-chatgpt-control.bundle.mjs +48392 -10260
- 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/chatgpt-url.d.ts +2 -0
- package/dist/src/browser/chatgpt-url.js +9 -0
- package/dist/src/browser/downloads.d.ts +17 -1
- package/dist/src/browser/downloads.js +150 -28
- package/dist/src/browser/page-state.js +6 -0
- package/dist/src/client.d.ts +43 -0
- package/dist/src/client.js +1279 -59
- package/dist/src/commands/artifacts.js +6 -4
- package/dist/src/commands/chat-popover.d.ts +37 -0
- package/dist/src/commands/chat-popover.js +537 -0
- package/dist/src/commands/configuration.d.ts +5 -0
- package/dist/src/commands/configuration.js +134 -21
- package/dist/src/commands/context.js +10 -3
- package/dist/src/commands/doctor.d.ts +1 -1
- package/dist/src/commands/doctor.js +27 -4
- package/dist/src/commands/experience.d.ts +5 -0
- package/dist/src/commands/experience.js +104 -21
- package/dist/src/commands/files.js +14 -33
- package/dist/src/commands/modes.js +85 -21
- package/dist/src/commands/power-discovery.d.ts +130 -0
- package/dist/src/commands/power-discovery.js +669 -0
- package/dist/src/commands/project-sources.js +325 -31
- package/dist/src/commands/sequence.js +59 -32
- package/dist/src/commands/session.js +19 -6
- package/dist/src/commands/work.js +31 -0
- package/dist/src/dom/composer-text.d.ts +6 -0
- package/dist/src/dom/composer-text.js +85 -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 +167 -0
- package/dist/src/operations/browser-adapter.js +2103 -0
- package/dist/src/operations/browser-observation.d.ts +110 -0
- package/dist/src/operations/browser-observation.js +1256 -0
- package/dist/src/operations/browser-target.d.ts +105 -0
- package/dist/src/operations/browser-target.js +531 -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 +1232 -0
- package/dist/src/operations/client.d.ts +138 -0
- package/dist/src/operations/client.js +1145 -0
- package/dist/src/operations/collector.d.ts +195 -0
- package/dist/src/operations/collector.js +1039 -0
- package/dist/src/operations/configuration-routing.d.ts +4 -0
- package/dist/src/operations/configuration-routing.js +14 -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 +25 -0
- package/dist/src/operations/index.js +25 -0
- package/dist/src/operations/journal-authority.d.ts +12 -0
- package/dist/src/operations/journal-authority.js +1 -0
- package/dist/src/operations/journal-rpc-client.d.ts +12 -0
- package/dist/src/operations/journal-rpc-client.js +225 -0
- package/dist/src/operations/journal-rpc-protocol.d.ts +32 -0
- package/dist/src/operations/journal-rpc-protocol.js +214 -0
- package/dist/src/operations/journal-rpc-server.d.ts +15 -0
- package/dist/src/operations/journal-rpc-server.js +272 -0
- package/dist/src/operations/journal.d.ts +143 -0
- package/dist/src/operations/journal.js +1957 -0
- package/dist/src/operations/production-attachments.d.ts +118 -0
- package/dist/src/operations/production-attachments.js +1035 -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 +80 -0
- package/dist/src/operations/production-chatgpt-attachments.js +1639 -0
- package/dist/src/operations/production-configuration.d.ts +42 -0
- package/dist/src/operations/production-configuration.js +1544 -0
- package/dist/src/operations/production-primitives.d.ts +53 -0
- package/dist/src/operations/production-primitives.js +856 -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 +126 -0
- package/dist/src/operations/runtime-adapter.js +802 -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 +286 -0
- package/dist/src/operations/service.js +2831 -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/transactional-chat-power.d.ts +17 -0
- package/dist/src/operations/transactional-chat-power.js +212 -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 +41 -0
- package/dist/src/runtime/coordinated-page.js +651 -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/blockers.js +8 -1
- package/dist/src/safety/untrusted-output.js +2 -1
- package/dist/src/scripts/backend-server.js +4 -1
- package/dist/src/scripts/capture-surface-profile.js +7 -2
- package/dist/src/scripts/journal-server.d.ts +2 -0
- package/dist/src/scripts/journal-server.js +37 -0
- package/dist/src/scripts/live-smoke/harness.js +79 -7
- package/dist/src/scripts/live-smoke/scenarios.d.ts +42 -1
- package/dist/src/scripts/live-smoke/scenarios.js +147 -30
- package/dist/src/scripts/live-smoke/transactional.d.ts +17 -0
- package/dist/src/scripts/live-smoke/transactional.js +254 -0
- 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 +8 -3
- package/references/2026-08-16-transactional-operations.md +459 -0
- package/references/2026-09-06-journal-service.md +140 -0
- package/references/agents-runner.md +34 -0
- package/references/backend-protocol.md +176 -0
- package/references/python-parity.md +137 -0
- package/references/responses-adapter.md +19 -0
- package/references/streaming.md +6 -0
|
@@ -0,0 +1,802 @@
|
|
|
1
|
+
import { OperationRuntimeContext } from "../runtime/operation-context.js";
|
|
2
|
+
import { createOperationBrowserAdapter, OperationBrowserAdapterError } from "./browser-adapter.js";
|
|
3
|
+
import { CONTROL_POSTCONDITION_RETRY_POLICY } from "./control.js";
|
|
4
|
+
/** Stable, redacted error boundary for a request-scoped runtime adapter. */
|
|
5
|
+
export class OperationRuntimeAdapterError extends Error {
|
|
6
|
+
code;
|
|
7
|
+
constructor(code) {
|
|
8
|
+
super("The operation browser runtime could not prove the requested action safely.");
|
|
9
|
+
this.name = "OperationRuntimeAdapterError";
|
|
10
|
+
this.code = code;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* This is the generic adapter's injection inventory, retained for compatibility
|
|
15
|
+
* with integrations that assemble their own provider runtime. It does not
|
|
16
|
+
* describe the default ChatGPT composite: `chatgpt-runtime.ts` injects the
|
|
17
|
+
* proven production modules for these seams. The generic adapter itself never
|
|
18
|
+
* guesses selectors or routes legacy polling helpers through a tab actor.
|
|
19
|
+
*/
|
|
20
|
+
export const UNWIRED_OPERATION_RUNTIME_PRIMITIVES = Object.freeze([
|
|
21
|
+
"new_thread_creation",
|
|
22
|
+
"configuration_set",
|
|
23
|
+
"tool_selection",
|
|
24
|
+
"composer_population",
|
|
25
|
+
"file_chooser_handoff",
|
|
26
|
+
"send_activation",
|
|
27
|
+
"stop_activation",
|
|
28
|
+
"work_steer_activation"
|
|
29
|
+
]);
|
|
30
|
+
function isAbsolutePath(value) {
|
|
31
|
+
return value.startsWith("/")
|
|
32
|
+
|| /^[A-Za-z]:[\\/]/u.test(value)
|
|
33
|
+
|| value.startsWith("\\\\");
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Compose one lazy runtime capture over the existing operation browser
|
|
37
|
+
* adapter. The returned object is intentionally request-scoped: a caller
|
|
38
|
+
* should construct it in `OperationClient.adapterFactory` and retain it only
|
|
39
|
+
* for the resulting operation handle.
|
|
40
|
+
*/
|
|
41
|
+
export function createRuntimeOperationBrowserAdapter(options) {
|
|
42
|
+
options = normalizeRuntimeAdapterOptions(options);
|
|
43
|
+
let capturePromise;
|
|
44
|
+
let innerPromise;
|
|
45
|
+
let captureIdentity;
|
|
46
|
+
const exposeStaging = options.exposeStaging ?? options.primitives?.staging !== undefined;
|
|
47
|
+
const exposeControl = options.exposeControl ?? (options.primitives?.control !== undefined || options.recovery !== undefined);
|
|
48
|
+
const exposeArtifacts = options.exposeArtifacts === true && options.recovery === undefined;
|
|
49
|
+
const captureOnce = (request) => {
|
|
50
|
+
if (captureIdentity !== undefined
|
|
51
|
+
&& (captureIdentity.operationId !== request.operationId
|
|
52
|
+
|| captureIdentity.requestDigest !== request.requestDigest
|
|
53
|
+
|| captureIdentity.surface !== request.surface)) {
|
|
54
|
+
return Promise.reject(new OperationRuntimeAdapterError("capture_incomplete"));
|
|
55
|
+
}
|
|
56
|
+
assertNativeAbortSignal(request.signal);
|
|
57
|
+
captureIdentity ??= Object.freeze({
|
|
58
|
+
operationId: request.operationId,
|
|
59
|
+
requestDigest: request.requestDigest,
|
|
60
|
+
surface: request.surface
|
|
61
|
+
});
|
|
62
|
+
capturePromise ??= Promise.resolve()
|
|
63
|
+
.then(() => options.capture(Object.freeze({
|
|
64
|
+
operationId: request.operationId,
|
|
65
|
+
requestDigest: request.requestDigest,
|
|
66
|
+
surface: request.surface,
|
|
67
|
+
target: request.target,
|
|
68
|
+
signal: request.signal
|
|
69
|
+
})))
|
|
70
|
+
.then(capture => normalizeCapture(capture, options.primitives, options.recovery !== undefined))
|
|
71
|
+
.catch(error => {
|
|
72
|
+
if (error instanceof OperationRuntimeAdapterError)
|
|
73
|
+
throw error;
|
|
74
|
+
throw new OperationRuntimeAdapterError(captureErrorCode(error));
|
|
75
|
+
});
|
|
76
|
+
return capturePromise;
|
|
77
|
+
};
|
|
78
|
+
const initialize = async (request) => {
|
|
79
|
+
if (options.recovery !== undefined
|
|
80
|
+
&& (request.operationId !== options.recovery.operationId
|
|
81
|
+
|| request.requestDigest !== options.recovery.requestDigest
|
|
82
|
+
|| request.surface !== options.recovery.surface
|
|
83
|
+
|| !sameTargetRequest(request.target, options.recovery.targetRequest))) {
|
|
84
|
+
throw new OperationRuntimeAdapterError("target_binding_mismatch");
|
|
85
|
+
}
|
|
86
|
+
innerPromise ??= captureOnce(request).then(captured => {
|
|
87
|
+
const adapterOptions = {
|
|
88
|
+
page: captured.page,
|
|
89
|
+
...(captured.runtimeContext === undefined ? {} : { runtimeContext: captured.runtimeContext }),
|
|
90
|
+
owner: options.owner,
|
|
91
|
+
...(options.coordinator === undefined ? {} : { coordinator: options.coordinator }),
|
|
92
|
+
evidenceDigest: options.evidenceDigest,
|
|
93
|
+
...(captured.targetEvidence === undefined ? {} : { targetEvidence: captured.targetEvidence }),
|
|
94
|
+
...(captured.resolveTargetEvidence === undefined ? {} : { resolveTargetEvidence: captured.resolveTargetEvidence }),
|
|
95
|
+
...(captured.observeCurrentTarget === undefined ? {} : { observeCurrentTarget: captured.observeCurrentTarget }),
|
|
96
|
+
...(captured.capabilities === undefined ? {} : { capabilities: captured.capabilities }),
|
|
97
|
+
...(captured.newTargetAnchorDigest === undefined ? {} : { newTargetAnchorDigest: captured.newTargetAnchorDigest }),
|
|
98
|
+
...(captured.blankTaskEvidenceDigest === undefined ? {} : { blankTaskEvidenceDigest: captured.blankTaskEvidenceDigest }),
|
|
99
|
+
...(captured.authoritativeClaim === undefined ? {} : { authoritativeClaim: captured.authoritativeClaim }),
|
|
100
|
+
...(options.transactionTimeoutMs === undefined ? {} : { transactionTimeoutMs: options.transactionTimeoutMs }),
|
|
101
|
+
...(options.files === undefined ? {} : { files: options.files }),
|
|
102
|
+
...(options.fileManifestDigest === undefined ? {} : { fileManifestDigest: options.fileManifestDigest }),
|
|
103
|
+
...(captured.primitives.submission === undefined ? {} : { submission: captured.primitives.submission }),
|
|
104
|
+
...(captured.primitives.staging === undefined ? {} : { staging: captured.primitives.staging }),
|
|
105
|
+
...(captured.primitives.collector === undefined ? {} : { collector: captured.primitives.collector }),
|
|
106
|
+
...(captured.primitives.control === undefined ? {} : { control: captured.primitives.control }),
|
|
107
|
+
...(captured.primitives.artifacts === undefined ? {} : { artifacts: captured.primitives.artifacts }),
|
|
108
|
+
...(captured.outputDirectory === undefined ? {} : { outputDirectory: captured.outputDirectory }),
|
|
109
|
+
...(options.recovery === undefined ? {} : {
|
|
110
|
+
recovery: Object.freeze({
|
|
111
|
+
operationId: options.recovery.operationId,
|
|
112
|
+
requestDigest: options.recovery.requestDigest,
|
|
113
|
+
surface: options.recovery.surface,
|
|
114
|
+
target: options.recovery.target,
|
|
115
|
+
signal: request.signal
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
};
|
|
119
|
+
try {
|
|
120
|
+
return createOperationBrowserAdapter(adapterOptions);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
if (error instanceof OperationBrowserAdapterError) {
|
|
124
|
+
throw new OperationRuntimeAdapterError(mapAdapterError(error.code));
|
|
125
|
+
}
|
|
126
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
return await innerPromise;
|
|
130
|
+
};
|
|
131
|
+
const ensureRecovered = (operationId, requestDigest, signal) => {
|
|
132
|
+
const recovery = options.recovery;
|
|
133
|
+
if (recovery === undefined) {
|
|
134
|
+
return Promise.reject(new OperationRuntimeAdapterError("not_initialized"));
|
|
135
|
+
}
|
|
136
|
+
if (operationId !== recovery.operationId || requestDigest !== recovery.requestDigest) {
|
|
137
|
+
return Promise.reject(new OperationRuntimeAdapterError("capture_incomplete"));
|
|
138
|
+
}
|
|
139
|
+
assertNativeAbortSignal(signal);
|
|
140
|
+
return initialize({
|
|
141
|
+
operationId: recovery.operationId,
|
|
142
|
+
requestDigest: recovery.requestDigest,
|
|
143
|
+
surface: recovery.surface,
|
|
144
|
+
target: recovery.targetRequest,
|
|
145
|
+
signal
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
const resolveTarget = async (request) => {
|
|
149
|
+
const adapter = await initialize(request);
|
|
150
|
+
try {
|
|
151
|
+
return await adapter.resolveTarget(request);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
if (error instanceof OperationRuntimeAdapterError)
|
|
155
|
+
throw error;
|
|
156
|
+
if (error instanceof OperationBrowserAdapterError) {
|
|
157
|
+
throw new OperationRuntimeAdapterError(mapAdapterError(error.code));
|
|
158
|
+
}
|
|
159
|
+
throw new OperationRuntimeAdapterError("target_evidence_unavailable");
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
// Phase ports remain usable after a process/backend restart. In that case
|
|
163
|
+
// the lazy inner adapter has not been initialized by resolveTarget; recover
|
|
164
|
+
// the authenticated target first, then delegate the exact phase. Normal
|
|
165
|
+
// submit calls use the already-composed adapter. Every unavailable path
|
|
166
|
+
// returns a protocol-shaped redacted result rather than routing through the
|
|
167
|
+
// compatibility-only final transaction port.
|
|
168
|
+
const delegateSubmissionPhase = (request, callback, fallback) => {
|
|
169
|
+
if (options.recovery === undefined) {
|
|
170
|
+
return delegateSubmission(innerPromise, callback, fallback);
|
|
171
|
+
}
|
|
172
|
+
const signal = request.signal ?? new AbortController().signal;
|
|
173
|
+
return ensureRecovered(request.operationId, request.requestDigest, signal)
|
|
174
|
+
.then(callback)
|
|
175
|
+
.catch(() => fallback);
|
|
176
|
+
};
|
|
177
|
+
let recoveredCollector;
|
|
178
|
+
const submission = Object.freeze({
|
|
179
|
+
...(options.recoverAuthenticatedSend === undefined ? {} : { recoverAuthenticatedSend: async (request) => {
|
|
180
|
+
if (innerPromise !== undefined) {
|
|
181
|
+
return await (await innerPromise).submission.recoverSend(request);
|
|
182
|
+
}
|
|
183
|
+
const recovered = await options.recoverAuthenticatedSend(request);
|
|
184
|
+
if (recovered.result.status === "submitted" || recovered.result.status === "already_submitted") {
|
|
185
|
+
recoveredCollector = recovered.collector;
|
|
186
|
+
}
|
|
187
|
+
return recovered.result;
|
|
188
|
+
} }),
|
|
189
|
+
observeStaging: request => delegateSubmission(innerPromise, adapter => adapter.submission.observeStaging(request), unavailableStage()),
|
|
190
|
+
executeFileHandoffOnce: request => delegateSubmission(innerPromise, adapter => adapter.submission.executeFileHandoffOnce(request), unavailableHandoff()),
|
|
191
|
+
observeAttachments: request => delegateSubmission(innerPromise, adapter => adapter.submission.observeAttachments(request), { status: "unavailable" }),
|
|
192
|
+
prepareSend: request => delegateSubmissionPhase(request, adapter => adapter.submission.prepareSend(request), unavailablePrepareSend()),
|
|
193
|
+
executePreparedSend: request => delegateSubmissionPhase(request, adapter => adapter.submission.executePreparedSend(request), unavailableExecutePreparedSend()),
|
|
194
|
+
verifyPreparedSend: request => delegateSubmissionPhase(request, adapter => adapter.submission.verifyPreparedSend(request), unavailableFinalTransaction()),
|
|
195
|
+
recoverSend: request => delegateSubmissionPhase(request, adapter => adapter.submission.recoverSend(request), unavailableFinalTransaction()),
|
|
196
|
+
executeFinalTabTransaction: request => delegateSubmission(innerPromise, adapter => adapter.submission.executeFinalTabTransaction(request), { status: "blocked", blockerCode: "target_evidence_unavailable" })
|
|
197
|
+
});
|
|
198
|
+
const delegateRecovered = (operationId, requestDigest, signal, callback) => {
|
|
199
|
+
if (options.recovery === undefined)
|
|
200
|
+
return requireDelegate(innerPromise, callback);
|
|
201
|
+
return ensureRecovered(operationId, requestDigest, signal)
|
|
202
|
+
.then(callback)
|
|
203
|
+
.catch(error => {
|
|
204
|
+
if (error instanceof OperationRuntimeAdapterError)
|
|
205
|
+
throw error;
|
|
206
|
+
throw new OperationRuntimeAdapterError("target_evidence_unavailable");
|
|
207
|
+
});
|
|
208
|
+
};
|
|
209
|
+
const delegateRecoveredControl = (operationId, requestDigest, signal, callback, fallback) => {
|
|
210
|
+
if (options.recovery === undefined)
|
|
211
|
+
return delegateControl(innerPromise, callback, fallback);
|
|
212
|
+
return ensureRecovered(operationId, requestDigest, signal)
|
|
213
|
+
.then(adapter => callback(adapter) ?? fallback)
|
|
214
|
+
.catch(() => fallback);
|
|
215
|
+
};
|
|
216
|
+
const collector = Object.freeze({
|
|
217
|
+
readContext: request => recoveredCollector !== undefined ? recoveredCollector.readContext(request) : delegateRecovered(request.operationId, request.requestDigest, request.signal, adapter => adapter.collector.readContext(request)),
|
|
218
|
+
observe: request => recoveredCollector !== undefined ? recoveredCollector.observe(request) : delegateRecovered(request.operationId, request.requestDigest, request.signal, adapter => adapter.collector.observe(request)),
|
|
219
|
+
sleep: (milliseconds, signal) => recoveredCollector !== undefined ? recoveredCollector.sleep(milliseconds, signal) : requireDelegate(innerPromise, adapter => adapter.collector.sleep(milliseconds, signal))
|
|
220
|
+
});
|
|
221
|
+
const staging = Object.freeze({
|
|
222
|
+
readCurrent: request => delegateStaging(innerPromise, adapter => adapter.staging?.readCurrent(request), unavailableStaging(request)),
|
|
223
|
+
mutateOnce: request => delegateStagingMutation(innerPromise, adapter => adapter.staging?.mutateOnce(request)),
|
|
224
|
+
observe: request => delegateStaging(innerPromise, adapter => adapter.staging?.observe(request), unavailableStaging(request))
|
|
225
|
+
});
|
|
226
|
+
const control = Object.freeze({
|
|
227
|
+
postconditionRetry: CONTROL_POSTCONDITION_RETRY_POLICY,
|
|
228
|
+
observeTurn: request => delegateRecoveredControl(request.operationId, request.parentRequestDigest, request.signal, adapter => adapter.control?.observeTurn(request), { status: "uncertain", reason: "unavailable" }),
|
|
229
|
+
executeOnce: request => delegateRecoveredControl(request.operationId, request.parentRequestDigest, request.signal, adapter => adapter.control?.executeOnce(request), { status: "uncertain", blockerCode: "send_control_unavailable" }),
|
|
230
|
+
observePostcondition: request => delegateRecoveredControl(request.operationId, request.parentRequestDigest, request.signal, adapter => adapter.control?.observePostcondition(request), { status: "uncertain", blockerCode: "send_control_unavailable" }),
|
|
231
|
+
prepareSteer: request => delegateRecoveredControl(request.parentOperationId, request.parentRequestDigest, request.signal, adapter => adapter.control?.prepareSteer?.(request), unavailableSteerPhase(request, "prepare")),
|
|
232
|
+
executeSteerPrepared: request => delegateRecoveredControl(request.prepared.parentOperationId, request.prepared.parentRequestDigest, request.signal, adapter => adapter.control?.executeSteerPrepared?.(request), unavailableSteerPhase(request, "execute_prepared")),
|
|
233
|
+
verifySteer: request => delegateRecoveredControl(request.prepared.parentOperationId, request.prepared.parentRequestDigest, request.signal, adapter => adapter.control?.verifySteer?.(request), unavailableSteerPhase(request, "verify")),
|
|
234
|
+
recoverSteer: request => delegateRecoveredControl(request.prepared.parentOperationId, request.prepared.parentRequestDigest, request.signal, adapter => adapter.control?.recoverSteer?.(request), unavailableSteerPhase(request, "recovery"))
|
|
235
|
+
});
|
|
236
|
+
const artifacts = exposeArtifacts
|
|
237
|
+
? Object.freeze({
|
|
238
|
+
transfer: request => {
|
|
239
|
+
if (innerPromise === undefined) {
|
|
240
|
+
return Promise.reject(new OperationRuntimeAdapterError("not_initialized"));
|
|
241
|
+
}
|
|
242
|
+
return innerPromise.then(adapter => {
|
|
243
|
+
if (adapter.artifacts === undefined) {
|
|
244
|
+
throw new OperationRuntimeAdapterError("unsupported_browser_primitive");
|
|
245
|
+
}
|
|
246
|
+
return adapter.artifacts.transfer(request);
|
|
247
|
+
}).catch(error => {
|
|
248
|
+
if (error instanceof OperationRuntimeAdapterError)
|
|
249
|
+
throw error;
|
|
250
|
+
throw new OperationRuntimeAdapterError("target_evidence_unavailable");
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
: undefined;
|
|
255
|
+
const adapter = {
|
|
256
|
+
resolveTarget,
|
|
257
|
+
submission,
|
|
258
|
+
collector,
|
|
259
|
+
...(exposeStaging ? { staging } : {}),
|
|
260
|
+
...(exposeControl ? { control } : {}),
|
|
261
|
+
...(artifacts === undefined ? {} : { artifacts })
|
|
262
|
+
};
|
|
263
|
+
return Object.freeze(adapter);
|
|
264
|
+
}
|
|
265
|
+
/** Alias for callers that put the runtime qualifier first. */
|
|
266
|
+
export const createOperationRuntimeAdapter = createRuntimeOperationBrowserAdapter;
|
|
267
|
+
function normalizeRuntimeAdapterOptions(value) {
|
|
268
|
+
if (!isPlainDataRecord(value))
|
|
269
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
270
|
+
try {
|
|
271
|
+
const owner = readOwnData(value, "owner");
|
|
272
|
+
const evidenceDigest = readOwnData(value, "evidenceDigest");
|
|
273
|
+
const capture = readOwnData(value, "capture");
|
|
274
|
+
const primitives = readOwnData(value, "primitives");
|
|
275
|
+
const exposeStaging = readOwnData(value, "exposeStaging");
|
|
276
|
+
const exposeControl = readOwnData(value, "exposeControl");
|
|
277
|
+
const exposeArtifacts = readOwnData(value, "exposeArtifacts");
|
|
278
|
+
const coordinator = readOwnData(value, "coordinator");
|
|
279
|
+
const transactionTimeoutMs = readOwnData(value, "transactionTimeoutMs");
|
|
280
|
+
const files = readOwnData(value, "files");
|
|
281
|
+
const fileManifestDigest = readOwnData(value, "fileManifestDigest");
|
|
282
|
+
const recovery = readOwnData(value, "recovery");
|
|
283
|
+
const recoverAuthenticatedSend = readOwnData(value, "recoverAuthenticatedSend");
|
|
284
|
+
const normalizedRecovery = recovery === undefined
|
|
285
|
+
? undefined
|
|
286
|
+
: (() => {
|
|
287
|
+
validateRecoveryContext(recovery);
|
|
288
|
+
return normalizeRecoveryContext(recovery);
|
|
289
|
+
})();
|
|
290
|
+
const snapshot = {
|
|
291
|
+
owner: cloneFrozenData(owner),
|
|
292
|
+
evidenceDigest,
|
|
293
|
+
capture,
|
|
294
|
+
primitives: primitives === undefined ? undefined : cloneFrozenProviderValue(primitives),
|
|
295
|
+
exposeStaging,
|
|
296
|
+
exposeControl,
|
|
297
|
+
exposeArtifacts,
|
|
298
|
+
coordinator,
|
|
299
|
+
transactionTimeoutMs,
|
|
300
|
+
files: files === undefined ? undefined : cloneFrozenData(files),
|
|
301
|
+
fileManifestDigest,
|
|
302
|
+
recovery: normalizedRecovery,
|
|
303
|
+
recoverAuthenticatedSend
|
|
304
|
+
};
|
|
305
|
+
const normalized = Object.freeze(snapshot);
|
|
306
|
+
validateOptions(normalized);
|
|
307
|
+
return normalized;
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
if (error instanceof OperationRuntimeAdapterError)
|
|
311
|
+
throw error;
|
|
312
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function validateOptions(options) {
|
|
316
|
+
if (!isPlainDataRecord(options)) {
|
|
317
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
318
|
+
}
|
|
319
|
+
if (typeof options.capture !== "function" || typeof options.evidenceDigest !== "function") {
|
|
320
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
321
|
+
}
|
|
322
|
+
if (options.recoverAuthenticatedSend !== undefined && typeof options.recoverAuthenticatedSend !== "function") {
|
|
323
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
324
|
+
}
|
|
325
|
+
if (options.owner === null || typeof options.owner !== "object" || typeof options.owner.backendSessionId !== "string") {
|
|
326
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
327
|
+
}
|
|
328
|
+
if (options.files !== undefined && !Array.isArray(options.files)) {
|
|
329
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
330
|
+
}
|
|
331
|
+
if (options.exposeStaging !== undefined && typeof options.exposeStaging !== "boolean") {
|
|
332
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
333
|
+
}
|
|
334
|
+
if (options.exposeControl !== undefined && typeof options.exposeControl !== "boolean") {
|
|
335
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
336
|
+
}
|
|
337
|
+
if (options.exposeArtifacts !== undefined && typeof options.exposeArtifacts !== "boolean") {
|
|
338
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
339
|
+
}
|
|
340
|
+
if (options.recovery !== undefined && options.exposeArtifacts === true) {
|
|
341
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
342
|
+
}
|
|
343
|
+
if (options.recovery !== undefined)
|
|
344
|
+
validateRecoveryContext(options.recovery);
|
|
345
|
+
}
|
|
346
|
+
function validateRecoveryContext(value) {
|
|
347
|
+
if (!isPlainDataRecord(value))
|
|
348
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
349
|
+
const operationId = readOwnData(value, "operationId");
|
|
350
|
+
const requestDigest = readOwnData(value, "requestDigest");
|
|
351
|
+
const surface = readOwnData(value, "surface");
|
|
352
|
+
const target = readOwnData(value, "target");
|
|
353
|
+
const targetRequest = readOwnData(value, "targetRequest");
|
|
354
|
+
if (typeof operationId !== "string"
|
|
355
|
+
|| !/^[A-Za-z0-9._:-]{1,512}$/u.test(operationId)
|
|
356
|
+
|| typeof requestDigest !== "string"
|
|
357
|
+
|| !/^hmac-sha256:[0-9a-f]{64}$/u.test(requestDigest)
|
|
358
|
+
|| (surface !== "chat" && surface !== "work")
|
|
359
|
+
|| !isPlainDataRecord(target)
|
|
360
|
+
|| !isPlainDataRecord(targetRequest)
|
|
361
|
+
|| !isSafeDataGraph(target)
|
|
362
|
+
|| !isSafeDataGraph(targetRequest)) {
|
|
363
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
364
|
+
}
|
|
365
|
+
const lifecycle = readOwnData(target, "targetLifecycle");
|
|
366
|
+
if (lifecycle === "new_pending") {
|
|
367
|
+
throw new OperationRuntimeAdapterError("target_binding_mismatch");
|
|
368
|
+
}
|
|
369
|
+
if (lifecycle !== undefined && lifecycle !== "fixed" && lifecycle !== "new_established") {
|
|
370
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
371
|
+
}
|
|
372
|
+
const targetType = readOwnData(targetRequest, "type");
|
|
373
|
+
if (targetType !== "new"
|
|
374
|
+
&& targetType !== "selected_tab"
|
|
375
|
+
&& targetType !== "tab_id"
|
|
376
|
+
&& targetType !== "conversation_id"
|
|
377
|
+
&& targetType !== "url") {
|
|
378
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
function normalizeRecoveryContext(value) {
|
|
382
|
+
try {
|
|
383
|
+
const record = value;
|
|
384
|
+
const operationId = readOwnData(record, "operationId");
|
|
385
|
+
const requestDigest = readOwnData(record, "requestDigest");
|
|
386
|
+
const surface = readOwnData(record, "surface");
|
|
387
|
+
const targetValue = readOwnData(record, "target");
|
|
388
|
+
const targetRequestValue = readOwnData(record, "targetRequest");
|
|
389
|
+
if (targetValue === undefined || targetRequestValue === undefined)
|
|
390
|
+
throw new Error("incomplete recovery context");
|
|
391
|
+
const target = cloneFrozenData(targetValue);
|
|
392
|
+
const targetRequest = cloneFrozenData(targetRequestValue);
|
|
393
|
+
return Object.freeze({
|
|
394
|
+
operationId: operationId,
|
|
395
|
+
requestDigest: requestDigest,
|
|
396
|
+
surface: surface,
|
|
397
|
+
target,
|
|
398
|
+
targetRequest
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
catch {
|
|
402
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
function sameTargetRequest(left, right) {
|
|
406
|
+
const leftType = readOwnData(left, "type");
|
|
407
|
+
const rightType = readOwnData(right, "type");
|
|
408
|
+
if (leftType !== rightType)
|
|
409
|
+
return false;
|
|
410
|
+
switch (leftType) {
|
|
411
|
+
case "tab_id":
|
|
412
|
+
case "conversation_id":
|
|
413
|
+
case "url":
|
|
414
|
+
return readOwnData(left, leftType === "tab_id" ? "tabId" : leftType === "conversation_id" ? "conversationId" : "url")
|
|
415
|
+
=== readOwnData(right, leftType === "tab_id" ? "tabId" : leftType === "conversation_id" ? "conversationId" : "url");
|
|
416
|
+
case "new":
|
|
417
|
+
case "selected_tab":
|
|
418
|
+
return true;
|
|
419
|
+
default:
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
function cloneFrozenData(value, seen = new Set()) {
|
|
424
|
+
if (value === null || typeof value !== "object") {
|
|
425
|
+
if (typeof value === "function" || typeof value === "symbol" || typeof value === "bigint")
|
|
426
|
+
throw new Error("unsupported value");
|
|
427
|
+
return value;
|
|
428
|
+
}
|
|
429
|
+
if (seen.has(value))
|
|
430
|
+
throw new Error("cyclic value");
|
|
431
|
+
seen.add(value);
|
|
432
|
+
try {
|
|
433
|
+
if (Array.isArray(value)) {
|
|
434
|
+
const result = value.map(item => cloneFrozenData(item, seen));
|
|
435
|
+
seen.delete(value);
|
|
436
|
+
return Object.freeze(result);
|
|
437
|
+
}
|
|
438
|
+
const prototype = Object.getPrototypeOf(value);
|
|
439
|
+
if (prototype !== Object.prototype && prototype !== null)
|
|
440
|
+
throw new Error("non-plain value");
|
|
441
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
442
|
+
// Keep descriptor reads side-effect free and define keys explicitly: an
|
|
443
|
+
// assignment to `__proto__` on a normal object invokes the legacy setter
|
|
444
|
+
// and silently drops the caller's own data property.
|
|
445
|
+
const result = Object.create(null);
|
|
446
|
+
for (const key of Object.keys(descriptors)) {
|
|
447
|
+
const descriptor = descriptors[key];
|
|
448
|
+
if (descriptor === undefined || !("value" in descriptor) || descriptor.get !== undefined || descriptor.set !== undefined)
|
|
449
|
+
throw new Error("accessor value");
|
|
450
|
+
Object.defineProperty(result, key, {
|
|
451
|
+
value: cloneFrozenData(descriptor.value, seen),
|
|
452
|
+
enumerable: descriptor.enumerable ?? false,
|
|
453
|
+
writable: true,
|
|
454
|
+
configurable: true
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
seen.delete(value);
|
|
458
|
+
return Object.freeze(result);
|
|
459
|
+
}
|
|
460
|
+
catch (error) {
|
|
461
|
+
seen.delete(value);
|
|
462
|
+
throw error;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
/** Clone callback/primitive records through descriptors, never through gets. */
|
|
466
|
+
function cloneFrozenProviderValue(value, seen = new Set()) {
|
|
467
|
+
if (value === null || typeof value !== "object")
|
|
468
|
+
return value;
|
|
469
|
+
if (seen.has(value))
|
|
470
|
+
throw new Error("cyclic provider value");
|
|
471
|
+
seen.add(value);
|
|
472
|
+
try {
|
|
473
|
+
if (Array.isArray(value)) {
|
|
474
|
+
const result = value.map(item => cloneFrozenProviderValue(item, seen));
|
|
475
|
+
seen.delete(value);
|
|
476
|
+
return Object.freeze(result);
|
|
477
|
+
}
|
|
478
|
+
const prototype = Object.getPrototypeOf(value);
|
|
479
|
+
if (prototype !== Object.prototype && prototype !== null)
|
|
480
|
+
throw new Error("non-plain provider value");
|
|
481
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
482
|
+
const result = Object.create(null);
|
|
483
|
+
for (const key of Object.keys(descriptors)) {
|
|
484
|
+
const descriptor = descriptors[key];
|
|
485
|
+
if (descriptor === undefined || !("value" in descriptor) || descriptor.get !== undefined || descriptor.set !== undefined)
|
|
486
|
+
throw new Error("accessor provider value");
|
|
487
|
+
Object.defineProperty(result, key, {
|
|
488
|
+
value: cloneFrozenProviderValue(descriptor.value, seen),
|
|
489
|
+
enumerable: descriptor.enumerable ?? false,
|
|
490
|
+
writable: true,
|
|
491
|
+
configurable: true
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
seen.delete(value);
|
|
495
|
+
return Object.freeze(result);
|
|
496
|
+
}
|
|
497
|
+
catch (error) {
|
|
498
|
+
seen.delete(value);
|
|
499
|
+
throw error;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
function assertNativeAbortSignal(value) {
|
|
503
|
+
if (value === null || typeof value !== "object" || typeof AbortSignal !== "function") {
|
|
504
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
505
|
+
}
|
|
506
|
+
const getter = Object.getOwnPropertyDescriptor(AbortSignal.prototype, "aborted")?.get;
|
|
507
|
+
if (getter === undefined)
|
|
508
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
509
|
+
try {
|
|
510
|
+
if (typeof Reflect.apply(getter, value, []) !== "boolean")
|
|
511
|
+
throw new Error("invalid signal");
|
|
512
|
+
}
|
|
513
|
+
catch {
|
|
514
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
function normalizeCapture(value, configuredPrimitives, recoveryCapture = false) {
|
|
518
|
+
if (!isPlainDataRecord(value)) {
|
|
519
|
+
throw new OperationRuntimeAdapterError("capture_incomplete");
|
|
520
|
+
}
|
|
521
|
+
const page = readOwnData(value, "page");
|
|
522
|
+
const runtimeContext = readOwnData(value, "runtimeContext");
|
|
523
|
+
const targetEvidence = readOwnData(value, "targetEvidence");
|
|
524
|
+
const authoritativeClaim = readOwnData(value, "authoritativeClaim");
|
|
525
|
+
const capabilities = readOwnData(value, "capabilities");
|
|
526
|
+
const newTargetAnchorDigest = readOwnData(value, "newTargetAnchorDigest");
|
|
527
|
+
const blankTaskEvidenceDigest = readOwnData(value, "blankTaskEvidenceDigest");
|
|
528
|
+
const resolveTargetEvidence = readOwnData(value, "resolveTargetEvidence");
|
|
529
|
+
const observeCurrentTarget = readOwnData(value, "observeCurrentTarget");
|
|
530
|
+
const primitivesValue = readOwnData(value, "primitives");
|
|
531
|
+
if (page === undefined || page === null || typeof page !== "object" || Array.isArray(page)) {
|
|
532
|
+
throw new OperationRuntimeAdapterError("capture_incomplete");
|
|
533
|
+
}
|
|
534
|
+
if (runtimeContext !== undefined && !(runtimeContext instanceof OperationRuntimeContext)) {
|
|
535
|
+
throw new OperationRuntimeAdapterError("capture_incomplete");
|
|
536
|
+
}
|
|
537
|
+
if (typeof resolveTargetEvidence !== "function" && targetEvidence === undefined && !recoveryCapture) {
|
|
538
|
+
throw new OperationRuntimeAdapterError("target_evidence_unavailable");
|
|
539
|
+
}
|
|
540
|
+
if (resolveTargetEvidence !== undefined && typeof resolveTargetEvidence !== "function") {
|
|
541
|
+
throw new OperationRuntimeAdapterError("capture_incomplete");
|
|
542
|
+
}
|
|
543
|
+
if (observeCurrentTarget !== undefined && typeof observeCurrentTarget !== "function") {
|
|
544
|
+
throw new OperationRuntimeAdapterError("capture_incomplete");
|
|
545
|
+
}
|
|
546
|
+
if (recoveryCapture && typeof observeCurrentTarget !== "function") {
|
|
547
|
+
throw new OperationRuntimeAdapterError("target_evidence_unavailable");
|
|
548
|
+
}
|
|
549
|
+
if (primitivesValue !== undefined && !isPlainDataRecord(primitivesValue)) {
|
|
550
|
+
throw new OperationRuntimeAdapterError("capture_incomplete");
|
|
551
|
+
}
|
|
552
|
+
const primitives = (primitivesValue === undefined
|
|
553
|
+
? {}
|
|
554
|
+
: cloneFrozenProviderValue(primitivesValue));
|
|
555
|
+
if (configuredPrimitives !== undefined && !isPlainDataRecord(configuredPrimitives)) {
|
|
556
|
+
throw new OperationRuntimeAdapterError("adapter_incomplete");
|
|
557
|
+
}
|
|
558
|
+
const mergedPrimitives = {};
|
|
559
|
+
const configuredSubmission = configuredPrimitives === undefined
|
|
560
|
+
? undefined
|
|
561
|
+
: readOwnData(configuredPrimitives, "submission");
|
|
562
|
+
const configuredStaging = configuredPrimitives === undefined
|
|
563
|
+
? undefined
|
|
564
|
+
: readOwnData(configuredPrimitives, "staging");
|
|
565
|
+
const configuredCollector = configuredPrimitives === undefined
|
|
566
|
+
? undefined
|
|
567
|
+
: readOwnData(configuredPrimitives, "collector");
|
|
568
|
+
const configuredControl = configuredPrimitives === undefined
|
|
569
|
+
? undefined
|
|
570
|
+
: readOwnData(configuredPrimitives, "control");
|
|
571
|
+
const configuredArtifacts = configuredPrimitives === undefined
|
|
572
|
+
? undefined
|
|
573
|
+
: readOwnData(configuredPrimitives, "artifacts");
|
|
574
|
+
const submission = (readOwnData(primitives, "submission") ?? configuredSubmission);
|
|
575
|
+
if (submission !== undefined)
|
|
576
|
+
mergedPrimitives.submission = submission;
|
|
577
|
+
const staging = (readOwnData(primitives, "staging") ?? configuredStaging);
|
|
578
|
+
if (staging !== undefined)
|
|
579
|
+
mergedPrimitives.staging = staging;
|
|
580
|
+
const collector = (readOwnData(primitives, "collector") ?? configuredCollector);
|
|
581
|
+
if (collector !== undefined)
|
|
582
|
+
mergedPrimitives.collector = collector;
|
|
583
|
+
const control = (readOwnData(primitives, "control") ?? configuredControl);
|
|
584
|
+
if (control !== undefined)
|
|
585
|
+
mergedPrimitives.control = control;
|
|
586
|
+
// A restart capture never supplies request-local artifact primitives. Do not
|
|
587
|
+
// resurrect an artifact source from static options or durable state.
|
|
588
|
+
if (!recoveryCapture) {
|
|
589
|
+
const artifacts = (readOwnData(primitives, "artifacts") ?? configuredArtifacts);
|
|
590
|
+
if (artifacts !== undefined)
|
|
591
|
+
mergedPrimitives.artifacts = artifacts;
|
|
592
|
+
}
|
|
593
|
+
const outputDirectory = readOwnData(value, "outputDirectory");
|
|
594
|
+
if (outputDirectory !== undefined
|
|
595
|
+
&& (typeof outputDirectory !== "string" || outputDirectory.length === 0 || outputDirectory.length > 4096
|
|
596
|
+
|| !isAbsolutePath(outputDirectory) || /[\u0000-\u001f\u007f]/u.test(outputDirectory))) {
|
|
597
|
+
throw new OperationRuntimeAdapterError("capture_incomplete");
|
|
598
|
+
}
|
|
599
|
+
if (recoveryCapture && outputDirectory !== undefined) {
|
|
600
|
+
throw new OperationRuntimeAdapterError("capture_incomplete");
|
|
601
|
+
}
|
|
602
|
+
const normalizedTargetEvidence = targetEvidence === undefined ? undefined : cloneFrozenData(targetEvidence);
|
|
603
|
+
const normalizedClaim = authoritativeClaim === undefined ? undefined : cloneFrozenData(authoritativeClaim);
|
|
604
|
+
const normalizedCapabilities = capabilities === undefined ? undefined : cloneFrozenData(capabilities);
|
|
605
|
+
return Object.freeze({
|
|
606
|
+
page: page,
|
|
607
|
+
...(runtimeContext === undefined ? {} : { runtimeContext }),
|
|
608
|
+
...(normalizedTargetEvidence === undefined ? {} : { targetEvidence: normalizedTargetEvidence }),
|
|
609
|
+
...(normalizedClaim === undefined ? {} : { authoritativeClaim: normalizedClaim }),
|
|
610
|
+
...(normalizedCapabilities === undefined ? {} : { capabilities: normalizedCapabilities }),
|
|
611
|
+
...(newTargetAnchorDigest === undefined ? {} : { newTargetAnchorDigest: newTargetAnchorDigest }),
|
|
612
|
+
...(blankTaskEvidenceDigest === undefined ? {} : { blankTaskEvidenceDigest: blankTaskEvidenceDigest }),
|
|
613
|
+
...(resolveTargetEvidence === undefined ? {} : { resolveTargetEvidence: resolveTargetEvidence }),
|
|
614
|
+
...(observeCurrentTarget === undefined ? {} : { observeCurrentTarget: observeCurrentTarget }),
|
|
615
|
+
...(outputDirectory === undefined ? {} : { outputDirectory }),
|
|
616
|
+
primitives: Object.freeze(mergedPrimitives)
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
function isPlainDataRecord(value) {
|
|
620
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
621
|
+
return false;
|
|
622
|
+
try {
|
|
623
|
+
const prototype = Object.getPrototypeOf(value);
|
|
624
|
+
if (prototype !== Object.prototype && prototype !== null)
|
|
625
|
+
return false;
|
|
626
|
+
for (const descriptor of Object.values(Object.getOwnPropertyDescriptors(value))) {
|
|
627
|
+
if (!("value" in descriptor) || descriptor.get !== undefined || descriptor.set !== undefined)
|
|
628
|
+
return false;
|
|
629
|
+
}
|
|
630
|
+
return true;
|
|
631
|
+
}
|
|
632
|
+
catch {
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
function unavailableSteerPhase(request, phase) {
|
|
637
|
+
const identity = "prepared" in request ? request.prepared : request;
|
|
638
|
+
return Object.freeze({
|
|
639
|
+
schemaVersion: request.schemaVersion,
|
|
640
|
+
phase,
|
|
641
|
+
parentOperationId: identity.parentOperationId,
|
|
642
|
+
parentRequestDigest: identity.parentRequestDigest,
|
|
643
|
+
parentTargetBindingDigest: identity.parentTargetBindingDigest,
|
|
644
|
+
controlActionId: identity.controlActionId,
|
|
645
|
+
action: "steer",
|
|
646
|
+
requestDigest: identity.requestDigest,
|
|
647
|
+
expectedAssistantTurnId: identity.expectedAssistantTurnId,
|
|
648
|
+
...("prepared" in request ? {
|
|
649
|
+
assistantBranchId: request.prepared.assistantBranchId,
|
|
650
|
+
assistantParentTurnId: request.prepared.assistantParentTurnId,
|
|
651
|
+
baselineSnapshotDigest: request.prepared.baselineSnapshotDigest,
|
|
652
|
+
preparedDigest: request.prepared.preparedDigest
|
|
653
|
+
} : {}),
|
|
654
|
+
status: "blocked",
|
|
655
|
+
blockerCode: "backend_unavailable",
|
|
656
|
+
observationRequired: phase === "prepare" ? false : true,
|
|
657
|
+
mutationBoundary: phase === "prepare" ? "none" : "control_may_have_occurred"
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
function isSafeDataGraph(value, seen = new Set(), depth = 0) {
|
|
661
|
+
if (value === null || (typeof value !== "object" && typeof value !== "function"))
|
|
662
|
+
return true;
|
|
663
|
+
if (typeof value === "function")
|
|
664
|
+
return false;
|
|
665
|
+
if (depth > 16 || seen.has(value))
|
|
666
|
+
return false;
|
|
667
|
+
seen.add(value);
|
|
668
|
+
try {
|
|
669
|
+
const prototype = Object.getPrototypeOf(value);
|
|
670
|
+
if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null)
|
|
671
|
+
return false;
|
|
672
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
673
|
+
if (Object.keys(descriptors).length > 1024)
|
|
674
|
+
return false;
|
|
675
|
+
for (const descriptor of Object.values(descriptors)) {
|
|
676
|
+
if (!("value" in descriptor) || descriptor.get !== undefined || descriptor.set !== undefined)
|
|
677
|
+
return false;
|
|
678
|
+
if (!isSafeDataGraph(descriptor.value, seen, depth + 1))
|
|
679
|
+
return false;
|
|
680
|
+
}
|
|
681
|
+
return true;
|
|
682
|
+
}
|
|
683
|
+
catch {
|
|
684
|
+
return false;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
function readOwnData(value, key) {
|
|
688
|
+
try {
|
|
689
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
690
|
+
return descriptor !== undefined && "value" in descriptor && descriptor.get === undefined && descriptor.set === undefined
|
|
691
|
+
? descriptor.value
|
|
692
|
+
: undefined;
|
|
693
|
+
}
|
|
694
|
+
catch {
|
|
695
|
+
return undefined;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
function mapAdapterError(code) {
|
|
699
|
+
switch (code) {
|
|
700
|
+
case "adapter_incomplete": return "adapter_incomplete";
|
|
701
|
+
case "page_affinity_mismatch": return "page_affinity_mismatch";
|
|
702
|
+
case "target_evidence_unavailable": return "target_evidence_unavailable";
|
|
703
|
+
case "browser_bridge_unavailable": return "browser_bridge_unavailable";
|
|
704
|
+
case "unsupported_browser_primitive": return "unsupported_browser_primitive";
|
|
705
|
+
case "target_binding_mismatch": return "target_binding_mismatch";
|
|
706
|
+
case "input_file_changed": return "unsupported_browser_primitive";
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
function captureErrorCode(error) {
|
|
710
|
+
if (error === null || (typeof error !== "object" && typeof error !== "function"))
|
|
711
|
+
return "capture_failed";
|
|
712
|
+
const descriptor = Object.getOwnPropertyDescriptor(error, "code");
|
|
713
|
+
const code = descriptor !== undefined && "value" in descriptor && typeof descriptor.value === "string"
|
|
714
|
+
? descriptor.value
|
|
715
|
+
: undefined;
|
|
716
|
+
switch (code) {
|
|
717
|
+
case "backend_unavailable":
|
|
718
|
+
case "browser_bridge_unavailable":
|
|
719
|
+
case "login_required":
|
|
720
|
+
case "captcha":
|
|
721
|
+
case "rate_limited":
|
|
722
|
+
case "permission_required":
|
|
723
|
+
case "needs_confirmation":
|
|
724
|
+
case "runtime_incompatible":
|
|
725
|
+
case "target_evidence_unavailable":
|
|
726
|
+
case "target_binding_mismatch":
|
|
727
|
+
case "page_affinity_mismatch":
|
|
728
|
+
return code;
|
|
729
|
+
case "rate_limit":
|
|
730
|
+
return "rate_limited";
|
|
731
|
+
default:
|
|
732
|
+
return "capture_failed";
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
function requireDelegate(innerPromise, callback) {
|
|
736
|
+
if (innerPromise === undefined)
|
|
737
|
+
return Promise.reject(new OperationRuntimeAdapterError("not_initialized"));
|
|
738
|
+
return innerPromise
|
|
739
|
+
.then(callback)
|
|
740
|
+
.catch(error => {
|
|
741
|
+
if (error instanceof OperationRuntimeAdapterError)
|
|
742
|
+
throw error;
|
|
743
|
+
throw new OperationRuntimeAdapterError("target_evidence_unavailable");
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
function delegateSubmission(innerPromise, callback, fallback) {
|
|
747
|
+
if (innerPromise === undefined)
|
|
748
|
+
return Promise.resolve(fallback);
|
|
749
|
+
return innerPromise.then(callback).catch(() => fallback);
|
|
750
|
+
}
|
|
751
|
+
function delegateStaging(innerPromise, callback, fallback) {
|
|
752
|
+
if (innerPromise === undefined)
|
|
753
|
+
return Promise.resolve(fallback);
|
|
754
|
+
return innerPromise.then(adapter => callback(adapter) ?? fallback).catch(() => fallback);
|
|
755
|
+
}
|
|
756
|
+
function delegateStagingMutation(innerPromise, callback) {
|
|
757
|
+
if (innerPromise === undefined)
|
|
758
|
+
return Promise.reject(new OperationRuntimeAdapterError("not_initialized"));
|
|
759
|
+
return innerPromise.then(adapter => {
|
|
760
|
+
const result = callback(adapter);
|
|
761
|
+
if (result === undefined)
|
|
762
|
+
throw new OperationRuntimeAdapterError("unsupported_browser_primitive");
|
|
763
|
+
return result;
|
|
764
|
+
}).catch(error => {
|
|
765
|
+
if (error instanceof OperationRuntimeAdapterError)
|
|
766
|
+
throw error;
|
|
767
|
+
throw new OperationRuntimeAdapterError("target_evidence_unavailable");
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
function delegateControl(innerPromise, callback, fallback) {
|
|
771
|
+
if (innerPromise === undefined)
|
|
772
|
+
return Promise.resolve(fallback);
|
|
773
|
+
return innerPromise.then(adapter => callback(adapter) ?? fallback).catch(() => fallback);
|
|
774
|
+
}
|
|
775
|
+
function unavailableStage() {
|
|
776
|
+
return { status: "unavailable", reason: "unknown" };
|
|
777
|
+
}
|
|
778
|
+
function unavailableHandoff() {
|
|
779
|
+
return { status: "not_satisfied", blockerCode: "target_evidence_unavailable" };
|
|
780
|
+
}
|
|
781
|
+
function unavailablePrepareSend() {
|
|
782
|
+
return {
|
|
783
|
+
status: "blocked",
|
|
784
|
+
result: { status: "blocked", blockerCode: "target_evidence_unavailable" }
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
function unavailableExecutePreparedSend() {
|
|
788
|
+
return {
|
|
789
|
+
status: "blocked",
|
|
790
|
+
result: { status: "blocked", blockerCode: "target_evidence_unavailable" }
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
function unavailableFinalTransaction() {
|
|
794
|
+
return { status: "blocked", blockerCode: "target_evidence_unavailable" };
|
|
795
|
+
}
|
|
796
|
+
function unavailableStaging(request) {
|
|
797
|
+
return {
|
|
798
|
+
status: "unavailable",
|
|
799
|
+
desiredStateDigest: request.desiredStateDigest,
|
|
800
|
+
blockerCode: "target_evidence_unavailable"
|
|
801
|
+
};
|
|
802
|
+
}
|