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,314 @@
|
|
|
1
|
+
const MUTABLE_FIELDS = Object.freeze([
|
|
2
|
+
"browser",
|
|
3
|
+
"page",
|
|
4
|
+
"expectedTabId"
|
|
5
|
+
]);
|
|
6
|
+
const BASE_FIELDS = Object.freeze([
|
|
7
|
+
"agent",
|
|
8
|
+
"clipboard",
|
|
9
|
+
"now"
|
|
10
|
+
]);
|
|
11
|
+
const ALLOWED_OPTION_FIELDS = new Set([...BASE_FIELDS, ...MUTABLE_FIELDS]);
|
|
12
|
+
const STATIC_ERROR_MESSAGES = Object.freeze({
|
|
13
|
+
invalid_options: "RuntimeEnvSession options are invalid.",
|
|
14
|
+
invalid_capture: "RuntimeEnvSession capture contains an unsupported value.",
|
|
15
|
+
capture_closed: "RuntimeEnvSession capture is already closed.",
|
|
16
|
+
commit_conflict: "RuntimeEnvSession commit conflicts with a newer invocation.",
|
|
17
|
+
revision_exhausted: "RuntimeEnvSession revision capacity is exhausted."
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* Errors intentionally have a fixed message. In particular, no browser,
|
|
21
|
+
* page, tab id, caller object, or native error is interpolated into a
|
|
22
|
+
* RuntimeEnvSession diagnostic.
|
|
23
|
+
*/
|
|
24
|
+
export class RuntimeEnvSessionError extends Error {
|
|
25
|
+
code;
|
|
26
|
+
constructor(code) {
|
|
27
|
+
super(STATIC_ERROR_MESSAGES[code]);
|
|
28
|
+
this.name = "RuntimeEnvSessionError";
|
|
29
|
+
this.code = code;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function invalidOptions() {
|
|
33
|
+
return new RuntimeEnvSessionError("invalid_options");
|
|
34
|
+
}
|
|
35
|
+
function invalidCapture() {
|
|
36
|
+
return new RuntimeEnvSessionError("invalid_capture");
|
|
37
|
+
}
|
|
38
|
+
function readDataOptions(options) {
|
|
39
|
+
if (options === undefined)
|
|
40
|
+
return {};
|
|
41
|
+
if (options === null || typeof options !== "object" || Array.isArray(options)) {
|
|
42
|
+
throw invalidOptions();
|
|
43
|
+
}
|
|
44
|
+
let descriptors;
|
|
45
|
+
try {
|
|
46
|
+
// Reading descriptors does not invoke accessor values. A hostile proxy
|
|
47
|
+
// may still reject reflection; that is converted to the fixed error.
|
|
48
|
+
descriptors = Object.getOwnPropertyDescriptors(options);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
throw invalidOptions();
|
|
52
|
+
}
|
|
53
|
+
const result = {};
|
|
54
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
55
|
+
if (typeof key !== "string" || !ALLOWED_OPTION_FIELDS.has(key)) {
|
|
56
|
+
throw invalidOptions();
|
|
57
|
+
}
|
|
58
|
+
const descriptor = descriptors[key];
|
|
59
|
+
if (descriptor === undefined || descriptor.get !== undefined || descriptor.set !== undefined || !("value" in descriptor)) {
|
|
60
|
+
throw invalidOptions();
|
|
61
|
+
}
|
|
62
|
+
result[key] = descriptor;
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
function readOption(descriptors, key) {
|
|
67
|
+
return descriptors[key]?.value;
|
|
68
|
+
}
|
|
69
|
+
function validateOptions(options) {
|
|
70
|
+
const descriptors = readDataOptions(options);
|
|
71
|
+
const expectedTabId = readOption(descriptors, "expectedTabId");
|
|
72
|
+
if (expectedTabId !== undefined && typeof expectedTabId !== "string")
|
|
73
|
+
throw invalidOptions();
|
|
74
|
+
const now = readOption(descriptors, "now");
|
|
75
|
+
if (now !== undefined && typeof now !== "function")
|
|
76
|
+
throw invalidOptions();
|
|
77
|
+
return {
|
|
78
|
+
agent: readOption(descriptors, "agent"),
|
|
79
|
+
browser: readOption(descriptors, "browser"),
|
|
80
|
+
page: readOption(descriptors, "page"),
|
|
81
|
+
clipboard: readOption(descriptors, "clipboard"),
|
|
82
|
+
now,
|
|
83
|
+
expectedTabId
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function presence(value) {
|
|
87
|
+
return value === undefined ? "unset" : "set";
|
|
88
|
+
}
|
|
89
|
+
function sameValue(left, right) {
|
|
90
|
+
return Object.is(left, right);
|
|
91
|
+
}
|
|
92
|
+
function freezeFields(fields) {
|
|
93
|
+
return Object.freeze([...fields]);
|
|
94
|
+
}
|
|
95
|
+
function createInvocationEnv(base, snapshot) {
|
|
96
|
+
const env = {};
|
|
97
|
+
// Base/provider references are copied once and cannot be overwritten by a
|
|
98
|
+
// legacy command. Snapshot values are writable and are committed through
|
|
99
|
+
// the owning session's CAS path only.
|
|
100
|
+
for (const key of BASE_FIELDS) {
|
|
101
|
+
Object.defineProperty(env, key, {
|
|
102
|
+
configurable: false,
|
|
103
|
+
enumerable: true,
|
|
104
|
+
value: base[key],
|
|
105
|
+
writable: false
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
for (const key of MUTABLE_FIELDS) {
|
|
109
|
+
Object.defineProperty(env, key, {
|
|
110
|
+
configurable: false,
|
|
111
|
+
enumerable: true,
|
|
112
|
+
value: snapshot[key],
|
|
113
|
+
writable: true
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return env;
|
|
117
|
+
}
|
|
118
|
+
function readInvocationSnapshot(env) {
|
|
119
|
+
const snapshot = {};
|
|
120
|
+
for (const key of MUTABLE_FIELDS) {
|
|
121
|
+
let descriptor;
|
|
122
|
+
try {
|
|
123
|
+
descriptor = Object.getOwnPropertyDescriptor(env, key);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
throw invalidCapture();
|
|
127
|
+
}
|
|
128
|
+
// The fields are installed as data properties. Treat any attempted
|
|
129
|
+
// descriptor/prototype tampering as invalid without invoking a getter.
|
|
130
|
+
if (descriptor === undefined || descriptor.get !== undefined || descriptor.set !== undefined || !("value" in descriptor)) {
|
|
131
|
+
throw invalidCapture();
|
|
132
|
+
}
|
|
133
|
+
const value = descriptor.value;
|
|
134
|
+
if (key === "expectedTabId" && value !== undefined && typeof value !== "string") {
|
|
135
|
+
throw invalidCapture();
|
|
136
|
+
}
|
|
137
|
+
snapshot[key] = value;
|
|
138
|
+
}
|
|
139
|
+
return snapshot;
|
|
140
|
+
}
|
|
141
|
+
function freezeCommitResult(revision, changedFields, appliedFields, converged) {
|
|
142
|
+
return Object.freeze({
|
|
143
|
+
revision,
|
|
144
|
+
changedFields: freezeFields(changedFields),
|
|
145
|
+
appliedFields: freezeFields(appliedFields),
|
|
146
|
+
converged
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Owns the mutable browser/page/tab snapshot used by invocation-scoped
|
|
151
|
+
* RuntimeEnv captures. It intentionally performs no browser locking or
|
|
152
|
+
* command dispatch; this is the synchronous in-process snapshot/CAS boundary
|
|
153
|
+
* used by `createChatGPT` to isolate concurrent legacy invocations.
|
|
154
|
+
*/
|
|
155
|
+
export class RuntimeEnvSession {
|
|
156
|
+
base;
|
|
157
|
+
state;
|
|
158
|
+
captureCount = 0;
|
|
159
|
+
openCaptureCount = 0;
|
|
160
|
+
constructor(options) {
|
|
161
|
+
const validated = validateOptions(options);
|
|
162
|
+
this.base = Object.freeze({
|
|
163
|
+
agent: validated.agent,
|
|
164
|
+
clipboard: validated.clipboard,
|
|
165
|
+
now: validated.now
|
|
166
|
+
});
|
|
167
|
+
this.state = {
|
|
168
|
+
browser: validated.browser,
|
|
169
|
+
page: validated.page,
|
|
170
|
+
expectedTabId: validated.expectedTabId,
|
|
171
|
+
revision: 0
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/** Current revision; no browser or page value is exposed. */
|
|
175
|
+
get revision() {
|
|
176
|
+
return this.state.revision;
|
|
177
|
+
}
|
|
178
|
+
/** Return frozen, redacted state diagnostics. */
|
|
179
|
+
diagnostics() {
|
|
180
|
+
return Object.freeze({
|
|
181
|
+
revision: this.state.revision,
|
|
182
|
+
captures: this.captureCount,
|
|
183
|
+
openCaptures: this.openCaptureCount,
|
|
184
|
+
base: Object.freeze({
|
|
185
|
+
agent: presence(this.base.agent),
|
|
186
|
+
clipboard: presence(this.base.clipboard),
|
|
187
|
+
now: presence(this.base.now)
|
|
188
|
+
}),
|
|
189
|
+
snapshot: Object.freeze({
|
|
190
|
+
browser: presence(this.state.browser),
|
|
191
|
+
page: presence(this.state.page),
|
|
192
|
+
expectedTabId: presence(this.state.expectedTabId)
|
|
193
|
+
})
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
capture() {
|
|
197
|
+
const capturedRevision = this.state.revision;
|
|
198
|
+
const baseline = {
|
|
199
|
+
browser: this.state.browser,
|
|
200
|
+
page: this.state.page,
|
|
201
|
+
expectedTabId: this.state.expectedTabId
|
|
202
|
+
};
|
|
203
|
+
const env = createInvocationEnv(this.base, baseline);
|
|
204
|
+
let status = "open";
|
|
205
|
+
this.captureCount += 1;
|
|
206
|
+
this.openCaptureCount += 1;
|
|
207
|
+
const close = (nextStatus) => {
|
|
208
|
+
if (status !== "open")
|
|
209
|
+
throw new RuntimeEnvSessionError("capture_closed");
|
|
210
|
+
status = nextStatus;
|
|
211
|
+
this.openCaptureCount -= 1;
|
|
212
|
+
};
|
|
213
|
+
const commit = () => {
|
|
214
|
+
// A commit attempt is one-shot, including invalid captures and CAS
|
|
215
|
+
// conflicts. Retrying a mutated stale environment would be ambiguous.
|
|
216
|
+
close("committed");
|
|
217
|
+
const candidate = readInvocationSnapshot(env);
|
|
218
|
+
const changedFields = MUTABLE_FIELDS.filter((key) => !sameValue(candidate[key], baseline[key]));
|
|
219
|
+
// Read-only invocations never clobber a newer snapshot, regardless of
|
|
220
|
+
// how many commits occurred after this capture was made.
|
|
221
|
+
if (changedFields.length === 0) {
|
|
222
|
+
return freezeCommitResult(this.state.revision, changedFields, [], false);
|
|
223
|
+
}
|
|
224
|
+
const stale = capturedRevision !== this.state.revision;
|
|
225
|
+
if (stale) {
|
|
226
|
+
// Merge only fields intentionally changed by this invocation. A
|
|
227
|
+
// stale commit converges only if each such field already has the same
|
|
228
|
+
// reference/value in the current session state.
|
|
229
|
+
for (const key of changedFields) {
|
|
230
|
+
if (!sameValue(candidate[key], this.state[key])) {
|
|
231
|
+
throw new RuntimeEnvSessionError("commit_conflict");
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const appliedFields = changedFields.filter((key) => !sameValue(candidate[key], this.state[key]));
|
|
236
|
+
if (appliedFields.length === 0) {
|
|
237
|
+
return freezeCommitResult(this.state.revision, changedFields, appliedFields, stale);
|
|
238
|
+
}
|
|
239
|
+
if (this.state.revision >= Number.MAX_SAFE_INTEGER) {
|
|
240
|
+
throw new RuntimeEnvSessionError("revision_exhausted");
|
|
241
|
+
}
|
|
242
|
+
// The state object is replaced once, after every field has passed the
|
|
243
|
+
// CAS checks. Consumers can therefore never observe a partial tuple.
|
|
244
|
+
const nextState = {
|
|
245
|
+
browser: this.state.browser,
|
|
246
|
+
page: this.state.page,
|
|
247
|
+
expectedTabId: this.state.expectedTabId,
|
|
248
|
+
revision: this.state.revision + 1
|
|
249
|
+
};
|
|
250
|
+
for (const key of appliedFields) {
|
|
251
|
+
if (key === "browser")
|
|
252
|
+
nextState.browser = candidate.browser;
|
|
253
|
+
else if (key === "page")
|
|
254
|
+
nextState.page = candidate.page;
|
|
255
|
+
else
|
|
256
|
+
nextState.expectedTabId = candidate.expectedTabId;
|
|
257
|
+
}
|
|
258
|
+
this.state = nextState;
|
|
259
|
+
return freezeCommitResult(this.state.revision, changedFields, appliedFields, stale);
|
|
260
|
+
};
|
|
261
|
+
const abandon = () => {
|
|
262
|
+
close("abandoned");
|
|
263
|
+
};
|
|
264
|
+
const diagnostics = () => Object.freeze({
|
|
265
|
+
status,
|
|
266
|
+
revision: capturedRevision
|
|
267
|
+
});
|
|
268
|
+
return Object.freeze({
|
|
269
|
+
env,
|
|
270
|
+
revision: capturedRevision,
|
|
271
|
+
commit,
|
|
272
|
+
abandon,
|
|
273
|
+
diagnostics
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
/** Capture, run one invocation, and publish its snapshot only on success. */
|
|
277
|
+
async run(callback) {
|
|
278
|
+
if (typeof callback !== "function")
|
|
279
|
+
throw new RuntimeEnvSessionError("invalid_options");
|
|
280
|
+
const capture = this.capture();
|
|
281
|
+
try {
|
|
282
|
+
const result = await callback(capture.env);
|
|
283
|
+
try {
|
|
284
|
+
capture.commit();
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
// The snapshot is only a convenience default for a later invocation;
|
|
288
|
+
// it is not part of the browser command's outcome. A newer invocation
|
|
289
|
+
// may legitimately publish a different tab while this callback is in
|
|
290
|
+
// flight. Never turn an already-completed browser action into a
|
|
291
|
+
// rejected promise (and a tempting caller retry) merely because that
|
|
292
|
+
// stale convenience snapshot could not be published.
|
|
293
|
+
if (!(error instanceof RuntimeEnvSessionError) || error.code !== "commit_conflict") {
|
|
294
|
+
throw error;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return result;
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
// Preserve the callback/commit error exactly. Abandon is best effort
|
|
301
|
+
// and has a fixed error if the callback already closed the capture.
|
|
302
|
+
try {
|
|
303
|
+
capture.abandon();
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
// The original callback or commit error is the useful result.
|
|
307
|
+
}
|
|
308
|
+
throw error;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
export function createRuntimeEnvSession(options) {
|
|
313
|
+
return new RuntimeEnvSession(options);
|
|
314
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/** A stable provider/browser identity. Ephemeral tab labels are not accepted. */
|
|
2
|
+
export type StableBrowserIdentity = Readonly<{
|
|
3
|
+
providerId: string;
|
|
4
|
+
browserId: string;
|
|
5
|
+
}>;
|
|
6
|
+
/** A stable provider/browser/tab identity. The tab id must come from the provider. */
|
|
7
|
+
export type StableTabIdentity = Readonly<StableBrowserIdentity & {
|
|
8
|
+
tabId: string;
|
|
9
|
+
}>;
|
|
10
|
+
declare const browserResourceKeyBrand: unique symbol;
|
|
11
|
+
declare const tabResourceKeyBrand: unique symbol;
|
|
12
|
+
/** Opaque key for the short browser acquisition actor. */
|
|
13
|
+
export type BrowserResourceKey = string & {
|
|
14
|
+
readonly [browserResourceKeyBrand]: "BrowserResourceKey";
|
|
15
|
+
};
|
|
16
|
+
/** Opaque key for the process-scoped tab actor. */
|
|
17
|
+
export type TabResourceKey = string & {
|
|
18
|
+
readonly [tabResourceKeyBrand]: "TabResourceKey";
|
|
19
|
+
};
|
|
20
|
+
export type CoordinatorResourceKind = "browser" | "tab";
|
|
21
|
+
export type CoordinatorPriority = "read" | "mutation" | "control";
|
|
22
|
+
export type CoordinatorOwner = Readonly<{
|
|
23
|
+
/** Backend process/session that owns this SDK call. */
|
|
24
|
+
backendSessionId: string;
|
|
25
|
+
/** Optional operation or caller id used only for diagnostics. */
|
|
26
|
+
ownerId?: string;
|
|
27
|
+
operationId?: string;
|
|
28
|
+
}>;
|
|
29
|
+
export type CoordinatorTimingDiagnostics = {
|
|
30
|
+
readonly requestId: string;
|
|
31
|
+
readonly resourceKind: CoordinatorResourceKind;
|
|
32
|
+
readonly resourceKey: string;
|
|
33
|
+
readonly priority: CoordinatorPriority;
|
|
34
|
+
readonly owner: CoordinatorOwner;
|
|
35
|
+
readonly label?: string;
|
|
36
|
+
readonly enqueuedAt: number;
|
|
37
|
+
readonly deadlineAt?: number;
|
|
38
|
+
startedAt?: number;
|
|
39
|
+
/** Time the hierarchical browser gate admitted the callback. */
|
|
40
|
+
admittedAt?: number;
|
|
41
|
+
settledAt?: number;
|
|
42
|
+
queueDelayMs?: number;
|
|
43
|
+
/** Time spent waiting at the parent browser gate after this actor started. */
|
|
44
|
+
admissionDelayMs?: number;
|
|
45
|
+
executionMs?: number;
|
|
46
|
+
totalMs?: number;
|
|
47
|
+
queuedCancellation?: boolean;
|
|
48
|
+
queuedDeadlineExceeded?: boolean;
|
|
49
|
+
aborted?: boolean;
|
|
50
|
+
deadlineExceededInFlight?: boolean;
|
|
51
|
+
quarantinedUntilSettled?: boolean;
|
|
52
|
+
outcome?: "fulfilled" | "rejected";
|
|
53
|
+
};
|
|
54
|
+
export type CoordinatorAcquisitionContext = Readonly<{
|
|
55
|
+
resourceKind: CoordinatorResourceKind;
|
|
56
|
+
resourceKey: string;
|
|
57
|
+
acquisitionToken: string;
|
|
58
|
+
owner: CoordinatorOwner;
|
|
59
|
+
priority: CoordinatorPriority;
|
|
60
|
+
signal: AbortSignal;
|
|
61
|
+
timing: CoordinatorTimingDiagnostics;
|
|
62
|
+
}>;
|
|
63
|
+
export type CoordinatorQueueDiagnostics = Readonly<{
|
|
64
|
+
resourceKind: CoordinatorResourceKind;
|
|
65
|
+
resourceKey: string;
|
|
66
|
+
queueDepth: number;
|
|
67
|
+
active: boolean;
|
|
68
|
+
activeRequestId?: string;
|
|
69
|
+
activeOwner?: CoordinatorOwner;
|
|
70
|
+
completedCount: number;
|
|
71
|
+
rejectedCount: number;
|
|
72
|
+
lastCompleted?: CoordinatorTimingDiagnostics;
|
|
73
|
+
lastRejected?: CoordinatorTimingDiagnostics;
|
|
74
|
+
/** Present while a deadline-aborted callback is still settling. */
|
|
75
|
+
quarantinedUntilSettled?: CoordinatorTimingDiagnostics;
|
|
76
|
+
/**
|
|
77
|
+
* The browser-level parent gate for tab diagnostics, or the gate backing a
|
|
78
|
+
* browser actor's own diagnostics. This is intentionally a detached
|
|
79
|
+
* summary: callers must not be able to mutate scheduler state through
|
|
80
|
+
* diagnostics.
|
|
81
|
+
*/
|
|
82
|
+
browserGate?: CoordinatorBrowserGateDiagnostics;
|
|
83
|
+
}>;
|
|
84
|
+
export type CoordinatorBrowserGateDiagnostics = Readonly<{
|
|
85
|
+
resourceKind: "browser";
|
|
86
|
+
resourceKey: BrowserResourceKey;
|
|
87
|
+
queueDepth: number;
|
|
88
|
+
active: boolean;
|
|
89
|
+
activeSharedCount: number;
|
|
90
|
+
queuedExclusiveCount: number;
|
|
91
|
+
queuedSharedCount: number;
|
|
92
|
+
rejectedCount: number;
|
|
93
|
+
activeExclusiveRequestId?: string;
|
|
94
|
+
activeExclusiveOwner?: CoordinatorOwner;
|
|
95
|
+
}>;
|
|
96
|
+
export type CoordinatorRequestOptions = Readonly<{
|
|
97
|
+
owner: CoordinatorOwner;
|
|
98
|
+
priority?: CoordinatorPriority;
|
|
99
|
+
signal?: AbortSignal;
|
|
100
|
+
/** An absolute epoch-millisecond deadline. */
|
|
101
|
+
deadlineAt?: number;
|
|
102
|
+
/** A relative deadline. Cannot be combined with deadlineAt. */
|
|
103
|
+
timeoutMs?: number;
|
|
104
|
+
label?: string;
|
|
105
|
+
/** Explicit parent context for re-entry detection across async boundaries. */
|
|
106
|
+
acquisitionContext?: CoordinatorAcquisitionContext;
|
|
107
|
+
}>;
|
|
108
|
+
export type TabCoordinatorOptions = Readonly<{
|
|
109
|
+
/** Maximum number of queued (not active) calls per resource actor. */
|
|
110
|
+
maxQueueSize?: number;
|
|
111
|
+
/** Maximum consecutive reads before a waiting mutation is selected. */
|
|
112
|
+
maxConsecutiveReads?: number;
|
|
113
|
+
/** Maximum consecutive mutations before a waiting control is selected. */
|
|
114
|
+
maxConsecutiveMutations?: number;
|
|
115
|
+
/** Maximum consecutive controls before a waiting mutation is selected. */
|
|
116
|
+
maxConsecutiveControls?: number;
|
|
117
|
+
/** A waiting request older than this is selected by age, regardless of priority. */
|
|
118
|
+
maxWaitMs?: number;
|
|
119
|
+
/** Maximum consecutive browser-exclusive turns before a queued shared turn. */
|
|
120
|
+
maxConsecutiveBrowserExclusives?: number;
|
|
121
|
+
/** Maximum number of detached idle diagnostics retained for later inspection. */
|
|
122
|
+
maxIdleDiagnostics?: number;
|
|
123
|
+
now?: () => number;
|
|
124
|
+
}>;
|
|
125
|
+
export declare function createBrowserResourceKey(identity: StableBrowserIdentity): BrowserResourceKey;
|
|
126
|
+
export declare function createBrowserResourceKey(providerId: string, browserId: string): BrowserResourceKey;
|
|
127
|
+
export declare function createTabResourceKey(identity: StableTabIdentity): TabResourceKey;
|
|
128
|
+
export declare function createTabResourceKey(providerId: string, browserId: string, tabId: string): TabResourceKey;
|
|
129
|
+
export declare class CoordinatorError extends Error {
|
|
130
|
+
readonly code: string;
|
|
131
|
+
readonly diagnostics?: CoordinatorTimingDiagnostics | CoordinatorQueueDiagnostics;
|
|
132
|
+
constructor(code: string, message: string, diagnostics?: CoordinatorTimingDiagnostics | CoordinatorQueueDiagnostics);
|
|
133
|
+
}
|
|
134
|
+
export declare class InvalidResourceKeyError extends CoordinatorError {
|
|
135
|
+
constructor(message: string);
|
|
136
|
+
}
|
|
137
|
+
export declare class InvalidCoordinatorRequestError extends CoordinatorError {
|
|
138
|
+
constructor(message: string);
|
|
139
|
+
}
|
|
140
|
+
export declare class CoordinatorQueueFullError extends CoordinatorError {
|
|
141
|
+
constructor(diagnostics: CoordinatorQueueDiagnostics);
|
|
142
|
+
}
|
|
143
|
+
export type CoordinatorCancellationPhase = "queued" | "in_flight";
|
|
144
|
+
export declare class CoordinatorAbortedError extends CoordinatorError {
|
|
145
|
+
readonly phase: CoordinatorCancellationPhase;
|
|
146
|
+
constructor(phase: CoordinatorCancellationPhase, diagnostics: CoordinatorTimingDiagnostics);
|
|
147
|
+
}
|
|
148
|
+
export declare class CoordinatorDeadlineExceededError extends CoordinatorError {
|
|
149
|
+
readonly phase: CoordinatorCancellationPhase;
|
|
150
|
+
constructor(phase: CoordinatorCancellationPhase, diagnostics: CoordinatorTimingDiagnostics);
|
|
151
|
+
}
|
|
152
|
+
export declare class ReentrantAcquisitionError extends CoordinatorError {
|
|
153
|
+
readonly resourceKind: CoordinatorResourceKind;
|
|
154
|
+
readonly resourceKey: string;
|
|
155
|
+
constructor(context: CoordinatorAcquisitionContext);
|
|
156
|
+
}
|
|
157
|
+
type Callback<T, Context extends CoordinatorAcquisitionContext> = (context: Context) => T | PromiseLike<T>;
|
|
158
|
+
/**
|
|
159
|
+
* Process-local actors for short browser acquisition and tab transactions.
|
|
160
|
+
*
|
|
161
|
+
* The class intentionally coordinates only cooperating callers in this
|
|
162
|
+
* process. It does not advertise provider-level or cross-process tab
|
|
163
|
+
* concurrency; a provider claim/fencing capability must be integrated before
|
|
164
|
+
* those guarantees can be made. Callback code should perform one short
|
|
165
|
+
* browser operation. Polling, sleeps, journal I/O, hashing, and report work
|
|
166
|
+
* belong outside this API so no scheduler actor is held by those waits.
|
|
167
|
+
*/
|
|
168
|
+
export declare class ProcessTabCoordinator {
|
|
169
|
+
private readonly browserActors;
|
|
170
|
+
private readonly tabActors;
|
|
171
|
+
private readonly browserGates;
|
|
172
|
+
private readonly idleDiagnostics;
|
|
173
|
+
private readonly options;
|
|
174
|
+
constructor(options?: TabCoordinatorOptions);
|
|
175
|
+
withBrowserAcquisition<T>(resourceKey: BrowserResourceKey, options: CoordinatorRequestOptions, callback: Callback<T, CoordinatorAcquisitionContext>): Promise<T>;
|
|
176
|
+
withTabTransaction<T>(resourceKey: TabResourceKey, options: CoordinatorRequestOptions, callback: Callback<T, CoordinatorAcquisitionContext>): Promise<T>;
|
|
177
|
+
getBrowserDiagnostics(resourceKey: BrowserResourceKey): CoordinatorQueueDiagnostics;
|
|
178
|
+
getTabDiagnostics(resourceKey: TabResourceKey): CoordinatorQueueDiagnostics;
|
|
179
|
+
private getActor;
|
|
180
|
+
private createActor;
|
|
181
|
+
private diagnosticsKey;
|
|
182
|
+
private emptyDiagnostics;
|
|
183
|
+
private onActorIdle;
|
|
184
|
+
private getBrowserGate;
|
|
185
|
+
private maybeCleanupGate;
|
|
186
|
+
private enqueue;
|
|
187
|
+
}
|
|
188
|
+
/** Explicit factory to make process/runtime ownership visible at call sites. */
|
|
189
|
+
export declare function createProcessTabCoordinator(options?: TabCoordinatorOptions): ProcessTabCoordinator;
|
|
190
|
+
/**
|
|
191
|
+
* Return the lifecycle-wide coordinator used by default SDK/runtime services.
|
|
192
|
+
*
|
|
193
|
+
* Constructing a coordinator per client would make each queue internally
|
|
194
|
+
* correct while allowing two clients in the same backend process to overlap
|
|
195
|
+
* on the same tab. Callers that need deterministic test limits may still
|
|
196
|
+
* inject an explicitly constructed coordinator; production integration should
|
|
197
|
+
* use this shared instance.
|
|
198
|
+
*/
|
|
199
|
+
export declare function getProcessTabCoordinator(): ProcessTabCoordinator;
|
|
200
|
+
export {};
|