@openclaw/codex 2026.8.1-beta.2 → 2026.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.js +146 -0
- package/dist/{attempt-client-cleanup-DMLXUfa8.js → attempt-client-cleanup-CVPvp9dL.js} +47 -5
- package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
- package/dist/{binding-connection-I5Bo-AsX.js → binding-connection-CCQFs8lf.js} +15 -3
- package/dist/{bounded-turn-qgZmzy_4.js → bounded-turn-rq5tiSXk.js} +27 -26
- package/dist/{capabilities-CybGI1oz.js → capabilities-CGm_94eL.js} +1 -1
- package/dist/cli-metadata.js +1 -1
- package/dist/{command-formatters-B1AEz8ZA.js → command-formatters-C54Yg3eA.js} +2 -2
- package/dist/{command-handlers-Db8pu_ds.js → command-handlers-BvOAHTrA.js} +111 -67
- package/dist/{compact-LspQaGfl.js → compact-Hd7SAc0N.js} +35 -30
- package/dist/{session-binding-DPxIvMgE.js → config-Cxw3Cl3k.js} +361 -1161
- package/dist/context-compaction-activity-BOeJf7yo.js +58 -0
- package/dist/doctor-contract-api.js +128 -26
- package/dist/{dynamic-tools-BVdyWkUD.js → dynamic-tools-BFoUcRdx.js} +1341 -1464
- package/dist/{effective-mcp-catalog-DIIhAtOZ.js → effective-mcp-catalog-BTJHnDSx.js} +2 -2
- package/dist/elicitation-input-DMkeki8d.js +71 -0
- package/dist/{event-projector-assistant-message-BwxV4d6N.js → event-projector-assistant-message-C5-Ds8mA.js} +18 -1
- package/dist/{event-projector-usage-e5huIQWu.js → event-projector-usage-D3PHFFXh.js} +18 -18
- package/dist/harness-DVfUQBlQ.js +248 -0
- package/dist/harness.js +2 -226
- package/dist/index.js +405 -299
- package/dist/{isolated-completion-C58NbL40.js → isolated-completion-Cbbx_5Ed.js} +4 -4
- package/dist/launch-args-sXOAg0SX.js +84 -0
- package/dist/managed-binary-ZkGodW19.js +248 -0
- package/dist/media-understanding-provider.js +2 -2
- package/dist/model-catalog-o6Cgjoss.js +101 -0
- package/dist/{models-BFlpn1Ex.js → models-DgFWSuxA.js} +25 -28
- package/dist/{native-subagent-monitor-DyTGmTUx.js → native-subagent-monitor-CwFyoylz.js} +385 -94
- package/dist/node-exec-server.runtime-DXrqpw1c.js +195 -0
- package/dist/plugin-approval-roundtrip-D80UZiJ_.js +95 -0
- package/dist/{plugin-marketplace-discovery-CzrGJdCR.js → plugin-marketplace-discovery-mdmScYOj.js} +60 -55
- package/dist/{plugin-metadata-cache-CNJPj4_b.js → plugin-metadata-cache-CulOABwu.js} +164 -134
- package/dist/{protocol-validators-B4F7L8Rf.js → protocol-validators-Bg5rpNxk.js} +203 -7
- package/dist/{rate-limits-DZXmOL8-.js → rate-limits-hFtzt2Up.js} +1 -1
- package/dist/{sandbox-guard-Cmwc9eIk.js → request-CCm4LaED.js} +207 -20
- package/dist/{run-attempt-P8BdxC1I.js → run-attempt-BwNQDH2u.js} +1506 -836
- package/dist/{runtime-artifact-BVSPdd6g.js → runtime-artifact-DeBvleJw.js} +4 -7
- package/dist/session-binding-q2WSTVmC.js +947 -0
- package/dist/{session-catalog-xwNi_SDM.js → session-catalog-C2Tae4rI.js} +1698 -1346
- package/dist/session-catalog-home-id-DAKUrm3C.js +112 -0
- package/dist/{session-cli-7hgXZDV7.js → session-cli-BuDjm5Ou.js} +20 -7
- package/dist/session-permission-policy-gLm40Vtq.js +160 -0
- package/dist/session-retirement-CdkI0bvb.js +90 -0
- package/dist/{session-upstream-marker-CrIFX06J.js → session-upstream-marker-Bcy33kam.js} +2 -3
- package/dist/{settled-turn-finalizer-BRWBO4MO.js → settled-turn-finalizer-B8JJcxCi.js} +27 -22
- package/dist/{shared-client-kIBbGjk4.js → shared-client-kNkljY-R.js} +2023 -607
- package/dist/{side-question-9HAL26l9.js → side-question-GwD73tX6.js} +109 -57
- package/dist/{thread-lifecycle-Bu1BTDXr.js → thread-lifecycle-Ss-uwnC4.js} +1208 -497
- package/dist/{thread-ownership-6kOQHlyC.js → thread-ownership-CeYBqg_4.js} +18 -8
- package/dist/{plugin-app-cache-key-C3H2CVp6.js → thread-resume-DF54hagX.js} +64 -6
- package/dist/{transcript-mirror-B9gGgnJS.js → transcript-mirror-CkEQIna-.js} +158 -41
- package/dist/transport-stdio-C7-s9TLG.js +708 -0
- package/dist/{upstream-prompt-provenance-ClRBije0.js → upstream-prompt-provenance-CFmsKwr9.js} +12 -14
- package/dist/{upstream-session-fork-CE-_LdnP.js → upstream-session-fork-BACGlOFa.js} +86 -88
- package/dist/{usage-CHlV0e-o.js → usage-C2l8Mils.js} +3 -3
- package/dist/{web-search-provider.runtime-DRw2Vl42.js → web-search-provider.runtime-B2XgRdFv.js} +3 -3
- package/openclaw.plugin.json +50 -2
- package/package.json +9 -8
- package/dist/app-server-policy-zcBDc1JU.js +0 -49
- package/dist/incognito-session-ET_ufDH-.js +0 -2
- package/dist/request-BRl5_UfX.js +0 -157
- package/dist/session-retirement-DoMMJFKm.js +0 -36
- package/dist/thread-resume-CPrJNQVM.js +0 -30
- package/dist/transport-stdio-DIImwDVS.js +0 -99
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Ht as isJsonObject } from "./shared-client-kNkljY-R.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import fsSync from "node:fs";
|
|
5
|
+
import { root } from "openclaw/plugin-sdk/file-access-runtime";
|
|
6
|
+
import { createZstdDecompress } from "node:zlib";
|
|
7
|
+
//#region extensions/codex/src/session-catalog-provenance.ts
|
|
8
|
+
const MAX_SESSION_META_BYTES = 1024 * 1024;
|
|
9
|
+
const SESSION_META_READ_CHUNK_BYTES = 64 * 1024;
|
|
10
|
+
const MAX_PROVENANCE_CACHE_ENTRIES = 2e4;
|
|
11
|
+
const provenanceByPath = /* @__PURE__ */ new Map();
|
|
12
|
+
function cacheProvenance(key, value) {
|
|
13
|
+
provenanceByPath.delete(key);
|
|
14
|
+
provenanceByPath.set(key, value);
|
|
15
|
+
while (provenanceByPath.size > MAX_PROVENANCE_CACHE_ENTRIES) {
|
|
16
|
+
const oldest = provenanceByPath.keys().next().value;
|
|
17
|
+
if (oldest === void 0) break;
|
|
18
|
+
provenanceByPath.delete(oldest);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Undefined means the metadata line is not durable enough to cache yet. */
|
|
22
|
+
async function readCodexSessionMeta(sessionsRoot, rolloutPath, threadId) {
|
|
23
|
+
let safeRoot;
|
|
24
|
+
try {
|
|
25
|
+
safeRoot = await root(sessionsRoot, {
|
|
26
|
+
hardlinks: "reject",
|
|
27
|
+
maxBytes: Number.MAX_SAFE_INTEGER,
|
|
28
|
+
symlinks: "reject"
|
|
29
|
+
});
|
|
30
|
+
} catch {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const candidates = rolloutPath.endsWith(".zst") ? [rolloutPath, rolloutPath.slice(0, -4)] : [rolloutPath, `${rolloutPath}.zst`];
|
|
34
|
+
for (const candidate of candidates) {
|
|
35
|
+
let opened;
|
|
36
|
+
try {
|
|
37
|
+
opened = await safeRoot.open(path.relative(sessionsRoot, candidate));
|
|
38
|
+
} catch {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const input = opened.handle.createReadStream({
|
|
42
|
+
autoClose: false,
|
|
43
|
+
highWaterMark: SESSION_META_READ_CHUNK_BYTES
|
|
44
|
+
});
|
|
45
|
+
const reader = candidate.endsWith(".zst") ? input.pipe(createZstdDecompress()) : input;
|
|
46
|
+
try {
|
|
47
|
+
const chunks = [];
|
|
48
|
+
let bytesReadTotal = 0;
|
|
49
|
+
let line;
|
|
50
|
+
for await (const value of reader) {
|
|
51
|
+
const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
52
|
+
const remaining = MAX_SESSION_META_BYTES - bytesReadTotal;
|
|
53
|
+
if (remaining <= 0) break;
|
|
54
|
+
const bounded = chunk.subarray(0, remaining);
|
|
55
|
+
bytesReadTotal += bounded.length;
|
|
56
|
+
const newline = bounded.indexOf(10);
|
|
57
|
+
chunks.push(newline >= 0 ? bounded.subarray(0, newline) : bounded);
|
|
58
|
+
if (newline >= 0) {
|
|
59
|
+
line = Buffer.concat(chunks).toString("utf8");
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!line) continue;
|
|
64
|
+
let parsed;
|
|
65
|
+
try {
|
|
66
|
+
parsed = JSON.parse(line);
|
|
67
|
+
} catch {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (!isJsonObject(parsed) || parsed.type !== "session_meta" || !isJsonObject(parsed.payload)) return null;
|
|
71
|
+
const payload = parsed.payload;
|
|
72
|
+
return payload.id === threadId ? payload : null;
|
|
73
|
+
} catch {
|
|
74
|
+
continue;
|
|
75
|
+
} finally {
|
|
76
|
+
reader.destroy();
|
|
77
|
+
input.destroy();
|
|
78
|
+
await opened.handle.close().catch(() => void 0);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Codex 0.147 reports OpenClaw app-server rollouts as `vscode`, so the rollout's
|
|
84
|
+
* immutable session metadata is the authoritative historical provenance.
|
|
85
|
+
*/
|
|
86
|
+
async function isOpenClawManagedCodexThread(thread, localSessionsRoot) {
|
|
87
|
+
const rolloutPath = typeof thread.path === "string" ? thread.path.trim() : "";
|
|
88
|
+
if (!localSessionsRoot || !rolloutPath) return false;
|
|
89
|
+
const cacheKey = `${localSessionsRoot}\0${rolloutPath}`;
|
|
90
|
+
const cached = provenanceByPath.get(cacheKey);
|
|
91
|
+
if (cached !== void 0) return cached;
|
|
92
|
+
const metadata = await readCodexSessionMeta(localSessionsRoot, rolloutPath, thread.id);
|
|
93
|
+
const managed = metadata === void 0 ? void 0 : metadata?.originator === "openclaw";
|
|
94
|
+
if (managed !== void 0) cacheProvenance(cacheKey, managed);
|
|
95
|
+
return managed ?? false;
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region extensions/codex/src/session-catalog-home-id.ts
|
|
99
|
+
function canonicalCodexCatalogHome(value) {
|
|
100
|
+
const resolved = path.resolve(value);
|
|
101
|
+
try {
|
|
102
|
+
return fsSync.realpathSync.native(resolved);
|
|
103
|
+
} catch {
|
|
104
|
+
return resolved;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/** One canonical identity for catalog discovery and durable ownership rows. */
|
|
108
|
+
function codexCatalogHomeId(codexHome) {
|
|
109
|
+
return createHash("sha256").update("openclaw:codex-session-catalog-home:v1\0").update(canonicalCodexCatalogHome(codexHome)).digest("hex");
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
112
|
+
export { readCodexSessionMeta as i, codexCatalogHomeId as n, isOpenClawManagedCodexThread as r, canonicalCodexCatalogHome as t };
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "./session-catalog-C2Tae4rI.js";
|
|
2
2
|
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
3
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
4
4
|
import { sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
|
|
5
|
+
import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
|
|
5
6
|
import { addGatewayClientOptions, callGatewayFromCli } from "openclaw/plugin-sdk/gateway-runtime";
|
|
6
7
|
//#region extensions/codex/src/session-cli.ts
|
|
7
8
|
const CODEX_SESSION_CATALOG_CLI_TIMEOUT_MS = 75e3;
|
|
9
|
+
function requestedAgentId(options) {
|
|
10
|
+
const agentId = options.agent?.trim();
|
|
11
|
+
return agentId ? normalizeAgentId(agentId) : void 0;
|
|
12
|
+
}
|
|
8
13
|
function writeLine(value = "") {
|
|
9
14
|
process.stdout.write(`${value}\n`);
|
|
10
15
|
}
|
|
@@ -80,12 +85,14 @@ function filterHosts(result, selector) {
|
|
|
80
85
|
} : result;
|
|
81
86
|
}
|
|
82
87
|
async function listCodexSessions(options) {
|
|
88
|
+
const agentId = requestedAgentId(options);
|
|
83
89
|
const host = options.host?.trim() || void 0;
|
|
84
90
|
const cursor = options.cursor?.trim() || void 0;
|
|
85
91
|
if (cursor && !host) throw new Error("--cursor requires --host so the cursor is routed to one Codex host");
|
|
86
92
|
const search = options.search?.trim() || void 0;
|
|
87
93
|
const limitPerHost = parsePageLimit(options.limit);
|
|
88
94
|
const params = {
|
|
95
|
+
...agentId ? { agentId } : {},
|
|
89
96
|
...search ? { search } : {},
|
|
90
97
|
...limitPerHost !== void 0 ? { limitPerHost } : {},
|
|
91
98
|
...host ? { hostIds: [host] } : {},
|
|
@@ -122,10 +129,13 @@ function readThreadId(value) {
|
|
|
122
129
|
}
|
|
123
130
|
async function continueCodexSession(threadIdValue, options) {
|
|
124
131
|
const threadId = readThreadId(threadIdValue);
|
|
132
|
+
const agentId = requestedAgentId(options);
|
|
133
|
+
const hostId = options.host?.trim() || "gateway:local";
|
|
125
134
|
const raw = await callGatewayFromCli("sessions.catalog.continue", gatewayOptions(options), {
|
|
126
135
|
catalogId: "codex",
|
|
127
|
-
hostId
|
|
128
|
-
threadId
|
|
136
|
+
hostId,
|
|
137
|
+
threadId,
|
|
138
|
+
...agentId ? { agentId } : {}
|
|
129
139
|
}, {
|
|
130
140
|
mode: "cli",
|
|
131
141
|
scopes: ["operator.write"]
|
|
@@ -140,11 +150,14 @@ async function continueCodexSession(threadIdValue, options) {
|
|
|
140
150
|
}
|
|
141
151
|
async function archiveCodexSession(threadIdValue, options) {
|
|
142
152
|
const threadId = readThreadId(threadIdValue);
|
|
153
|
+
const agentId = requestedAgentId(options);
|
|
154
|
+
const hostId = options.host?.trim() || "gateway:local";
|
|
143
155
|
if (options.confirmNoOtherRunner !== true) throw new Error("--confirm-no-other-runner is required because Codex client and runner activity is process-local");
|
|
144
156
|
const raw = await callGatewayFromCli("sessions.catalog.archive", gatewayOptions(options), {
|
|
145
157
|
catalogId: "codex",
|
|
146
|
-
hostId
|
|
158
|
+
hostId,
|
|
147
159
|
threadId,
|
|
160
|
+
...agentId ? { agentId } : {},
|
|
148
161
|
confirmNoOtherRunner: true
|
|
149
162
|
}, {
|
|
150
163
|
mode: "cli",
|
|
@@ -161,13 +174,13 @@ async function archiveCodexSession(threadIdValue, options) {
|
|
|
161
174
|
/** Registers the plugin-owned Codex session supervision CLI. */
|
|
162
175
|
function registerCodexSessionCli(program) {
|
|
163
176
|
const codex = program.command("codex").description("Inspect and branch from Codex sessions through the Gateway");
|
|
164
|
-
addGatewayClientOptions(codex.command("sessions").description("List non-archived Codex app-server sessions across connected hosts").option("--search <text>", "Search session titles (case-insensitive)").option("--host <id>", "Filter by stable host id").option("--limit <count>", "Maximum sessions returned per host").option("--cursor <cursor>", "Continue one host page (requires --host)").option("--json", "Print the structured catalog response", false), { timeoutMs: CODEX_SESSION_CATALOG_CLI_TIMEOUT_MS }).action(async (options) => {
|
|
177
|
+
addGatewayClientOptions(codex.command("sessions").description("List non-archived Codex app-server sessions across connected hosts").option("--agent <id>", "Agent id that owns the Codex sessions").option("--search <text>", "Search session titles (case-insensitive)").option("--host <id>", "Filter by stable host id").option("--limit <count>", "Maximum sessions returned per host").option("--cursor <cursor>", "Continue one host page (requires --host)").option("--json", "Print the structured catalog response", false), { timeoutMs: CODEX_SESSION_CATALOG_CLI_TIMEOUT_MS }).action(async (options) => {
|
|
165
178
|
await listCodexSessions(options);
|
|
166
179
|
});
|
|
167
|
-
addGatewayClientOptions(codex.command("continue <thread-id>").description("Continue a Gateway-local Codex thread as an OpenClaw branch").option("--json", "Print the structured response", false)).action(async (threadId, options) => {
|
|
180
|
+
addGatewayClientOptions(codex.command("continue <thread-id>").description("Continue a Gateway-local Codex thread as an OpenClaw branch").option("--agent <id>", "Agent id that owns the Codex session").option("--host <id>", "Stable local host id from codex sessions").option("--json", "Print the structured response", false)).action(async (threadId, options) => {
|
|
168
181
|
await continueCodexSession(threadId, options);
|
|
169
182
|
});
|
|
170
|
-
addGatewayClientOptions(codex.command("archive <thread-id>").description("Archive a stored or idle Gateway-local Codex thread").option("--confirm-no-other-runner", "Confirm no other Codex client or OpenClaw runner is using this thread", false).option("--json", "Print the structured response", false)).action(async (threadId, options) => {
|
|
183
|
+
addGatewayClientOptions(codex.command("archive <thread-id>").description("Archive a stored or idle Gateway-local Codex thread").option("--agent <id>", "Agent id that owns the Codex session").option("--host <id>", "Stable local host id from codex sessions").option("--confirm-no-other-runner", "Confirm no other Codex client or OpenClaw runner is using this thread", false).option("--json", "Print the structured response", false)).action(async (threadId, options) => {
|
|
171
184
|
await archiveCodexSession(threadId, options);
|
|
172
185
|
});
|
|
173
186
|
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { O as selectGuardianSandbox, _ as parseAllowedSandboxModesFromCodexRequirements, b as selectGuardianApprovalsReviewer, g as parseAllowedApprovalsReviewersFromCodexRequirements, h as parseAllowedApprovalPoliciesFromCodexRequirements, l as resolveCodexAppServerNetworkProxy, p as canUseCodexModelBackedApprovalsReviewerForModel, x as selectUserApprovalsReviewer, y as selectGuardianApprovalPolicy } from "./config-Cxw3Cl3k.js";
|
|
2
|
+
import { hostname } from "node:os";
|
|
3
|
+
import { isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
|
|
4
|
+
//#region extensions/codex/src/app-server/app-server-policy.ts
|
|
5
|
+
function resolveCodexAppServerForModelProvider(params) {
|
|
6
|
+
const explicitProvider = normalizeModelBackedReviewerProvider(params.provider);
|
|
7
|
+
if (!isCodexModelBackedApprovalsReviewer(params.appServer.approvalsReviewer) || canUseCodexModelBackedApprovalsReviewerForModel({
|
|
8
|
+
modelProvider: explicitProvider,
|
|
9
|
+
model: params.model,
|
|
10
|
+
config: params.config,
|
|
11
|
+
env: {
|
|
12
|
+
...params.env ?? process.env,
|
|
13
|
+
...params.appServer.start.env
|
|
14
|
+
},
|
|
15
|
+
agentDir: params.agentDir,
|
|
16
|
+
codexConfigToml: params.codexConfigToml,
|
|
17
|
+
homeScope: params.appServer.start.homeScope,
|
|
18
|
+
codexArgs: params.appServer.start.args
|
|
19
|
+
})) return params.appServer;
|
|
20
|
+
return {
|
|
21
|
+
...params.appServer,
|
|
22
|
+
approvalsReviewer: "user"
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function isCodexModelBackedApprovalsReviewer(value) {
|
|
26
|
+
return value === "auto_review" || value === "guardian_subagent";
|
|
27
|
+
}
|
|
28
|
+
function normalizeModelBackedReviewerProvider(provider) {
|
|
29
|
+
return provider?.trim().toLowerCase() || void 0;
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region extensions/codex/src/app-server/session-permission-policy.ts
|
|
33
|
+
const CODEX_SESSION_PERMISSION_EXEC_MODES = {
|
|
34
|
+
"read-only": "deny",
|
|
35
|
+
guarded: "ask",
|
|
36
|
+
workspace: "auto",
|
|
37
|
+
full: "full"
|
|
38
|
+
};
|
|
39
|
+
function tupleForMode(mode, canUseAutoReview) {
|
|
40
|
+
switch (mode) {
|
|
41
|
+
case "read-only": return {
|
|
42
|
+
sandbox: "read-only",
|
|
43
|
+
approvalPolicy: "on-request",
|
|
44
|
+
approvalsReviewer: "user"
|
|
45
|
+
};
|
|
46
|
+
case "guarded": return {
|
|
47
|
+
sandbox: "workspace-write",
|
|
48
|
+
approvalPolicy: "on-request",
|
|
49
|
+
approvalsReviewer: "user"
|
|
50
|
+
};
|
|
51
|
+
case "workspace": return {
|
|
52
|
+
sandbox: "workspace-write",
|
|
53
|
+
approvalPolicy: "on-request",
|
|
54
|
+
approvalsReviewer: canUseAutoReview ? "auto_review" : "user"
|
|
55
|
+
};
|
|
56
|
+
case "full": return {
|
|
57
|
+
sandbox: "danger-full-access",
|
|
58
|
+
approvalPolicy: "never",
|
|
59
|
+
approvalsReviewer: "user"
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return mode;
|
|
63
|
+
}
|
|
64
|
+
function requirementsAllowTuple(tuple, allowed) {
|
|
65
|
+
return (allowed.sandboxes === void 0 || allowed.sandboxes.has(tuple.sandbox)) && (allowed.approvalPolicies === void 0 || allowed.approvalPolicies.has(tuple.approvalPolicy)) && (allowed.reviewers === void 0 || allowed.reviewers.has(tuple.approvalsReviewer));
|
|
66
|
+
}
|
|
67
|
+
function tightenTupleForExecMode(tuple, execMode, requiresPerCommandApproval) {
|
|
68
|
+
switch (execMode) {
|
|
69
|
+
case "deny":
|
|
70
|
+
case "allowlist": return {
|
|
71
|
+
sandbox: "read-only",
|
|
72
|
+
approvalPolicy: "on-request",
|
|
73
|
+
approvalsReviewer: "user"
|
|
74
|
+
};
|
|
75
|
+
case "ask": return {
|
|
76
|
+
...tuple,
|
|
77
|
+
approvalPolicy: requiresPerCommandApproval ? "untrusted" : "on-request",
|
|
78
|
+
approvalsReviewer: "user"
|
|
79
|
+
};
|
|
80
|
+
case "auto":
|
|
81
|
+
case "full":
|
|
82
|
+
case void 0: return tuple;
|
|
83
|
+
}
|
|
84
|
+
return execMode;
|
|
85
|
+
}
|
|
86
|
+
function clampSessionPermissionTuple(params) {
|
|
87
|
+
if (!params.requirementsToml) return params.requested;
|
|
88
|
+
const allowed = {
|
|
89
|
+
sandboxes: parseAllowedSandboxModesFromCodexRequirements(params.requirementsToml, params.hostName ?? hostname()),
|
|
90
|
+
approvalPolicies: parseAllowedApprovalPoliciesFromCodexRequirements(params.requirementsToml),
|
|
91
|
+
reviewers: parseAllowedApprovalsReviewersFromCodexRequirements(params.requirementsToml)
|
|
92
|
+
};
|
|
93
|
+
if (requirementsAllowTuple(params.requested, allowed)) return params.requested;
|
|
94
|
+
if (params.requested.approvalPolicy === "untrusted" && allowed.approvalPolicies?.has("untrusted") === false) throw new Error("tools.exec.ask=always requires Codex app-server per-command approvals");
|
|
95
|
+
const userReviewRequired = params.mode === "read-only" || params.mode === "guarded" || params.mode === "workspace" && !params.canUseAutoReview;
|
|
96
|
+
const sandboxAuthority = {
|
|
97
|
+
"read-only": 0,
|
|
98
|
+
"workspace-write": 1,
|
|
99
|
+
"danger-full-access": 2
|
|
100
|
+
};
|
|
101
|
+
const allowedSandboxes = new Set([
|
|
102
|
+
"read-only",
|
|
103
|
+
"workspace-write",
|
|
104
|
+
"danger-full-access"
|
|
105
|
+
].filter((sandbox) => sandboxAuthority[sandbox] <= sandboxAuthority[params.requested.sandbox] && (allowed.sandboxes === void 0 || allowed.sandboxes.has(sandbox))));
|
|
106
|
+
if (allowedSandboxes.size === 0) throw new Error(`Codex session permission mode=${params.mode} cannot satisfy managed sandbox requirements without widening access`);
|
|
107
|
+
return {
|
|
108
|
+
sandbox: selectGuardianSandbox(allowedSandboxes),
|
|
109
|
+
approvalPolicy: params.requested.approvalPolicy === "untrusted" ? "untrusted" : selectGuardianApprovalPolicy(allowed.approvalPolicies, userReviewRequired ? "ask" : "auto"),
|
|
110
|
+
approvalsReviewer: userReviewRequired ? selectUserApprovalsReviewer(allowed.reviewers) : selectGuardianApprovalsReviewer(allowed.reviewers, "auto")
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/** Applies one complete session-mode tuple without mixing requirements-clamped fields. */
|
|
114
|
+
function applyCodexSessionPermissionPolicy(params) {
|
|
115
|
+
if (!params.permissionMode) return params.appServer;
|
|
116
|
+
const sessionRoot = params.sessionRoot?.trim() || params.defaultRoot;
|
|
117
|
+
if (params.policyLocked) return {
|
|
118
|
+
...params.appServer,
|
|
119
|
+
sessionRoot
|
|
120
|
+
};
|
|
121
|
+
const requested = tightenTupleForExecMode(tupleForMode(params.permissionMode, params.canUseAutoReview), params.execMode, params.appServer.approvalPolicy === "untrusted");
|
|
122
|
+
const tuple = params.appServer.start.transport === "stdio" ? clampSessionPermissionTuple({
|
|
123
|
+
mode: params.permissionMode,
|
|
124
|
+
requested,
|
|
125
|
+
requirementsToml: params.requirementsToml,
|
|
126
|
+
hostName: params.hostName,
|
|
127
|
+
canUseAutoReview: params.canUseAutoReview
|
|
128
|
+
}) : requested;
|
|
129
|
+
const networkProxy = params.appServer.networkProxy ? resolveCodexAppServerNetworkProxy(params.pluginConfig.appServer?.networkProxy, tuple.sandbox).networkProxy : void 0;
|
|
130
|
+
const resolved = {
|
|
131
|
+
...params.appServer,
|
|
132
|
+
...tuple,
|
|
133
|
+
sessionRoot
|
|
134
|
+
};
|
|
135
|
+
if (networkProxy) resolved.networkProxy = networkProxy;
|
|
136
|
+
else delete resolved.networkProxy;
|
|
137
|
+
return resolved;
|
|
138
|
+
}
|
|
139
|
+
function resolveCodexEffectiveSessionPermissionPolicy(params) {
|
|
140
|
+
if (!params.permissionMode) return;
|
|
141
|
+
const root = params.sessionRoot?.trim() || params.defaultRoot;
|
|
142
|
+
const { sandbox, approvalPolicy, approvalsReviewer } = params.appServer;
|
|
143
|
+
const fullAccess = params.permissionMode === "full" && sandbox === "danger-full-access" && approvalPolicy === "never";
|
|
144
|
+
const guardianReview = params.permissionMode !== "guarded" && sandbox === "workspace-write" && approvalPolicy === "on-request" && approvalsReviewer !== "user";
|
|
145
|
+
const mode = params.permissionMode === "read-only" || sandbox === "read-only" ? "read-only" : fullAccess ? "full" : guardianReview ? "workspace" : "guarded";
|
|
146
|
+
return {
|
|
147
|
+
mode,
|
|
148
|
+
root,
|
|
149
|
+
execMode: CODEX_SESSION_PERMISSION_EXEC_MODES[mode]
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/** Keeps relative execution inside the prepared root without filesystem rediscovery. */
|
|
153
|
+
function resolveCodexSessionPermissionCwd(params) {
|
|
154
|
+
if (!params.permissionMode) return params.requestedCwd ?? params.fallbackCwd;
|
|
155
|
+
const sessionRoot = params.sessionRoot?.trim() || params.defaultRoot;
|
|
156
|
+
const requestedCwd = params.requestedCwd?.trim();
|
|
157
|
+
return requestedCwd && isPathInside(sessionRoot, requestedCwd) ? requestedCwd : sessionRoot;
|
|
158
|
+
}
|
|
159
|
+
//#endregion
|
|
160
|
+
export { resolveCodexAppServerForModelProvider as a, resolveCodexSessionPermissionCwd as i, applyCodexSessionPermissionPolicy as n, resolveCodexEffectiveSessionPermissionPolicy as r, CODEX_SESSION_PERMISSION_EXEC_MODES as t };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { St as releaseCodexAppServerLiveThread, _ as retainSharedCodexAppServerClientByInstanceId, bt as isCodexAppServerLiveThreadClaimed, vt as hasCodexAppServerLiveThread } from "./shared-client-kNkljY-R.js";
|
|
2
|
+
import { a as closeCodexStartupClientBestEffort, f as unsubscribeCodexThreadBestEffort, n as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, r as CodexAppServerUnsafeSubscriptionError } from "./attempt-client-cleanup-CVPvp9dL.js";
|
|
3
|
+
import { l as isIncognitoSessionKey, o as withCodexAppServerThreadMutation } from "./thread-ownership-CeYBqg_4.js";
|
|
4
|
+
import { t as codexNativeSubagentMonitorRuntime } from "./native-subagent-monitor-CwFyoylz.js";
|
|
5
|
+
//#region extensions/codex/src/app-server/session-retirement.ts
|
|
6
|
+
async function releaseSessionSubscription(client, binding, sessionKey, assertCurrent) {
|
|
7
|
+
assertCurrent?.();
|
|
8
|
+
codexNativeSubagentMonitorRuntime.retireParent(client, binding.threadId);
|
|
9
|
+
const released = await releaseCodexAppServerLiveThread(client, binding.threadId, assertCurrent);
|
|
10
|
+
assertCurrent?.();
|
|
11
|
+
if (!released && isIncognitoSessionKey(sessionKey)) {
|
|
12
|
+
const unsubscribed = await unsubscribeCodexThreadBestEffort(client, {
|
|
13
|
+
threadId: binding.threadId,
|
|
14
|
+
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS,
|
|
15
|
+
assertCurrent
|
|
16
|
+
});
|
|
17
|
+
assertCurrent?.();
|
|
18
|
+
if (!unsubscribed) {
|
|
19
|
+
await closeCodexStartupClientBestEffort(client);
|
|
20
|
+
throw new CodexAppServerUnsafeSubscriptionError(`Codex retired session subscription could not be released: ${binding.threadId}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** Prepare exact binding deletion before the session owner commits either database. */
|
|
25
|
+
async function withCodexAppServerSessionDeletion(bindingStore, params, run) {
|
|
26
|
+
const { assertCurrent } = params;
|
|
27
|
+
const identity = {
|
|
28
|
+
kind: "session",
|
|
29
|
+
agentId: params.agentId,
|
|
30
|
+
sessionKey: params.sessionKey,
|
|
31
|
+
sessionId: params.sessionId
|
|
32
|
+
};
|
|
33
|
+
return await bindingStore.withSessionDeletion(identity, assertCurrent, async (binding, mutation) => {
|
|
34
|
+
assertCurrent();
|
|
35
|
+
if (binding?.connectionScope === "supervision") throw new Error("Cannot delete a session while its Codex binding is owned by supervision");
|
|
36
|
+
const clientLease = binding?.clientId ? retainSharedCodexAppServerClientByInstanceId(binding.clientId) : void 0;
|
|
37
|
+
const assertUnclaimed = () => {
|
|
38
|
+
assertCurrent();
|
|
39
|
+
if (clientLease && binding && isCodexAppServerLiveThreadClaimed(clientLease.client, binding.threadId)) throw new Error("Cannot delete a session while its Codex thread is claimed by active work");
|
|
40
|
+
};
|
|
41
|
+
let committed = false;
|
|
42
|
+
try {
|
|
43
|
+
assertUnclaimed();
|
|
44
|
+
return await run({
|
|
45
|
+
commit() {
|
|
46
|
+
assertUnclaimed();
|
|
47
|
+
mutation.commit();
|
|
48
|
+
committed = true;
|
|
49
|
+
},
|
|
50
|
+
rollback() {
|
|
51
|
+
mutation.rollback();
|
|
52
|
+
committed = false;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
} finally {
|
|
56
|
+
try {
|
|
57
|
+
if (committed && binding && clientLease) await withCodexAppServerThreadMutation(binding.threadId, async () => {
|
|
58
|
+
assertCurrent();
|
|
59
|
+
if (!hasCodexAppServerLiveThread(clientLease.client, binding.threadId) && !isIncognitoSessionKey(params.sessionKey)) return;
|
|
60
|
+
if (await bindingStore.hasOtherThreadOwner(binding.threadId)) return;
|
|
61
|
+
await releaseSessionSubscription(clientLease.client, binding, params.sessionKey, assertUnclaimed);
|
|
62
|
+
});
|
|
63
|
+
} finally {
|
|
64
|
+
clientLease?.release();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/** Retire binding and native subscription under the same generation/physical-client ownership fence. */
|
|
70
|
+
async function retireCodexAppServerSessionGeneration(params) {
|
|
71
|
+
const retireGeneration = () => params.mode === "reset" ? params.bindingStore.resetSessionGeneration(params.identity) : params.bindingStore.retireSessionGeneration(params.identity);
|
|
72
|
+
const expectedBinding = await params.bindingStore.read(params.identity);
|
|
73
|
+
if (!expectedBinding) return await retireGeneration();
|
|
74
|
+
return await params.bindingStore.withLease(params.identity, async () => {
|
|
75
|
+
const binding = await params.bindingStore.read(params.identity);
|
|
76
|
+
if (binding?.threadId !== expectedBinding.threadId) return "conflict";
|
|
77
|
+
const result = await retireGeneration();
|
|
78
|
+
if (result !== "applied" || !binding?.clientId) return result;
|
|
79
|
+
const clientLease = retainSharedCodexAppServerClientByInstanceId(binding.clientId);
|
|
80
|
+
if (!clientLease) return result;
|
|
81
|
+
try {
|
|
82
|
+
await releaseSessionSubscription(clientLease.client, binding, params.identity.sessionKey);
|
|
83
|
+
} finally {
|
|
84
|
+
clientLease.release();
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
export { retireCodexAppServerSessionGeneration, withCodexAppServerSessionDeletion };
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import { r as importCodexThreadHistoryToTranscript } from "./transcript-mirror-
|
|
1
|
+
import { r as importCodexThreadHistoryToTranscript } from "./transcript-mirror-CkEQIna-.js";
|
|
2
2
|
import { resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
|
|
3
3
|
//#region extensions/codex/src/app-server/session-history-import.ts
|
|
4
4
|
/** Creates a session whose transcript is derived from one verified Codex thread snapshot. */
|
|
5
5
|
async function createImportedCodexSession(params) {
|
|
6
|
-
const label = params.thread.name?.trim() || void 0;
|
|
7
6
|
const spawnedCwd = params.thread.cwd?.trim() || void 0;
|
|
8
7
|
const createParams = {
|
|
9
8
|
cfg: params.config,
|
|
10
9
|
key: params.key,
|
|
11
10
|
agentId: params.agentId,
|
|
12
|
-
...
|
|
11
|
+
...params.displayName !== void 0 ? { displayName: params.displayName } : {},
|
|
13
12
|
...spawnedCwd ? { spawnedCwd } : {},
|
|
14
13
|
initialEntry: params.initialEntry,
|
|
15
14
|
afterCreate: async (entry) => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as readUpstreamUserText, r as readMirrorIdentity, t as attachCodexMirrorIdentity } from "./upstream-prompt-provenance-
|
|
3
|
-
import { c as
|
|
4
|
-
import { t as runBoundedCodexAppServerTurn } from "./bounded-turn-
|
|
5
|
-
import {
|
|
1
|
+
import { Ht as isJsonObject } from "./shared-client-kNkljY-R.js";
|
|
2
|
+
import { i as readUpstreamUserText, r as readMirrorIdentity, t as attachCodexMirrorIdentity } from "./upstream-prompt-provenance-CFmsKwr9.js";
|
|
3
|
+
import { c as fingerprintCodexMirrorSourceMessage, l as readCodexMirrorSourceFingerprint, t as codexTranscriptMirrorRuntime } from "./transcript-mirror-CkEQIna-.js";
|
|
4
|
+
import { t as runBoundedCodexAppServerTurn } from "./bounded-turn-rq5tiSXk.js";
|
|
5
|
+
import { r as createAssistantMessage } from "./event-projector-assistant-message-C5-Ds8mA.js";
|
|
6
6
|
import { isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
7
7
|
import { Buffer } from "node:buffer";
|
|
8
8
|
import { isSilentReplyText } from "openclaw/plugin-sdk/reply-runtime";
|
|
@@ -10,7 +10,7 @@ import { isSilentReplyText } from "openclaw/plugin-sdk/reply-runtime";
|
|
|
10
10
|
const MAX_RESPONSE_ITEMS = 200;
|
|
11
11
|
const MAX_PROJECTION_BYTES = 512 * 1024;
|
|
12
12
|
const MAX_TEXT_BYTES = 64 * 1024;
|
|
13
|
-
const TOOL_NAME_PATTERN = /^[a-zA-Z0-
|
|
13
|
+
const TOOL_NAME_PATTERN = /^[a-zA-Z0-9._-]{1,128}$/u;
|
|
14
14
|
const TOOL_ERROR_STATUS_PREFIX = "[Tool result status: error]\n";
|
|
15
15
|
function readBoundedText(value, label, maxBytes = MAX_TEXT_BYTES) {
|
|
16
16
|
if (typeof value !== "string" || !value.trim()) return;
|
|
@@ -32,7 +32,7 @@ function requireCallId(value) {
|
|
|
32
32
|
}
|
|
33
33
|
function requireToolName(value) {
|
|
34
34
|
const name = normalizeOptionalString(value);
|
|
35
|
-
if (!name || !TOOL_NAME_PATTERN.test(name)) throw new Error(
|
|
35
|
+
if (!name || !TOOL_NAME_PATTERN.test(name)) throw new Error(`Codex settled-turn projection found an invalid tool name${name ? `: ${name.slice(0, 64)}` : ""}`);
|
|
36
36
|
return name;
|
|
37
37
|
}
|
|
38
38
|
function serializeToolArguments(value) {
|
|
@@ -56,27 +56,26 @@ function serializeToolArguments(value) {
|
|
|
56
56
|
return requireBoundedText(serialized, "tool arguments");
|
|
57
57
|
}
|
|
58
58
|
function projectUserMessage(message) {
|
|
59
|
-
const record = message;
|
|
60
59
|
const upstreamUserText = readUpstreamUserText(message);
|
|
61
|
-
if (upstreamUserText && typeof
|
|
60
|
+
if (upstreamUserText && typeof message.content === "string") return [{
|
|
62
61
|
type: "message",
|
|
63
62
|
role: "user",
|
|
64
63
|
content: [{
|
|
65
64
|
type: "input_text",
|
|
66
|
-
text: requireBoundedText(upstreamUserText, "upstream user text")
|
|
65
|
+
text: requireBoundedText(upstreamUserText, "upstream user text", MAX_PROJECTION_BYTES)
|
|
67
66
|
}]
|
|
68
67
|
}];
|
|
69
|
-
if (typeof
|
|
68
|
+
if (typeof message.content === "string") return [{
|
|
70
69
|
type: "message",
|
|
71
70
|
role: "user",
|
|
72
71
|
content: [{
|
|
73
72
|
type: "input_text",
|
|
74
|
-
text: requireBoundedText(
|
|
73
|
+
text: requireBoundedText(message.content, "user message")
|
|
75
74
|
}]
|
|
76
75
|
}];
|
|
77
|
-
if (!Array.isArray(
|
|
76
|
+
if (!Array.isArray(message.content)) throw new Error("Codex settled-turn projection found unsupported user content");
|
|
78
77
|
const content = [];
|
|
79
|
-
for (const value of
|
|
78
|
+
for (const value of message.content) {
|
|
80
79
|
if (!isRecord(value)) throw new Error("Codex settled-turn projection found malformed user content");
|
|
81
80
|
if (value.type === "text") {
|
|
82
81
|
const text = readBoundedText(value.text, "user text");
|
|
@@ -86,7 +85,7 @@ function projectUserMessage(message) {
|
|
|
86
85
|
});
|
|
87
86
|
continue;
|
|
88
87
|
}
|
|
89
|
-
throw new Error(`Codex settled-turn projection does not support user content ${
|
|
88
|
+
throw new Error(`Codex settled-turn projection does not support user content ${value.type}`);
|
|
90
89
|
}
|
|
91
90
|
if (content.length === 0) throw new Error("Codex settled-turn projection found an empty user message");
|
|
92
91
|
return [{
|
|
@@ -144,8 +143,9 @@ function projectToolResult(message) {
|
|
|
144
143
|
const id = requireCallId(message.toolCallId);
|
|
145
144
|
const name = requireToolName(message.toolName);
|
|
146
145
|
if (!Array.isArray(message.content)) throw new Error("Codex settled-turn projection found unsupported tool result content");
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
const isErrorValue = message.isError;
|
|
147
|
+
if (isErrorValue !== void 0 && typeof isErrorValue !== "boolean") throw new Error("Codex settled-turn projection found invalid tool result status");
|
|
148
|
+
const isError = isErrorValue === true;
|
|
149
149
|
const parts = [];
|
|
150
150
|
for (const value of message.content) {
|
|
151
151
|
if (!isRecord(value)) throw new Error("Codex settled-turn projection found malformed tool result content");
|
|
@@ -173,17 +173,16 @@ function projectToolResult(message) {
|
|
|
173
173
|
};
|
|
174
174
|
}
|
|
175
175
|
function projectMessage(message) {
|
|
176
|
-
const record = message;
|
|
177
176
|
let items;
|
|
178
177
|
let calls = [];
|
|
179
178
|
let results = [];
|
|
180
179
|
if (message.role === "user") items = projectUserMessage(message);
|
|
181
180
|
else if (message.role === "assistant") {
|
|
182
|
-
const projected = projectAssistantMessage(
|
|
181
|
+
const projected = projectAssistantMessage(message);
|
|
183
182
|
items = projected.items;
|
|
184
183
|
calls = projected.calls;
|
|
185
184
|
} else if (message.role === "toolResult") {
|
|
186
|
-
const projected = projectToolResult(
|
|
185
|
+
const projected = projectToolResult(message);
|
|
187
186
|
items = [projected.item];
|
|
188
187
|
results = [projected.result];
|
|
189
188
|
} else throw new Error(`Codex settled-turn projection does not support role ${message.role}`);
|
|
@@ -304,6 +303,12 @@ async function runCodexSettledTurnFinalization(operation, options) {
|
|
|
304
303
|
cwd: attempt.workspaceDir,
|
|
305
304
|
messages: [assistant],
|
|
306
305
|
idempotencyScope: `codex-settled-finalizer:${attempt.runId}`,
|
|
306
|
+
runId: attempt.runId,
|
|
307
|
+
terminalAssistantOwner: {
|
|
308
|
+
mirrorIdentity,
|
|
309
|
+
runId: attempt.runId
|
|
310
|
+
},
|
|
311
|
+
prepareAssistantTranscriptMessage: attempt.prepareAssistantTranscriptMessage,
|
|
307
312
|
config: attempt.config,
|
|
308
313
|
skipBeforeMessageWriteHooks: true
|
|
309
314
|
});
|
|
@@ -311,8 +316,8 @@ async function runCodexSettledTurnFinalization(operation, options) {
|
|
|
311
316
|
const expectedFingerprint = fingerprintCodexMirrorSourceMessage(assistant);
|
|
312
317
|
if (!mirrorResult.assistantMirrorIdentitiesOwned.includes(mirrorIdentity) || !persistedMessage || persistedMessage.role !== "assistant" || readCodexMirrorSourceFingerprint(persistedMessage) !== expectedFingerprint) throw new Error("Codex settled-turn final answer transcript attestation mismatch");
|
|
313
318
|
const persistedAssistant = persistedMessage;
|
|
314
|
-
const
|
|
315
|
-
const assistantTranscriptIdempotencyKey = typeof
|
|
319
|
+
const persistedIdempotencyKey = "idempotencyKey" in persistedAssistant ? persistedAssistant.idempotencyKey : void 0;
|
|
320
|
+
const assistantTranscriptIdempotencyKey = typeof persistedIdempotencyKey === "string" ? persistedIdempotencyKey.trim() : "";
|
|
316
321
|
return {
|
|
317
322
|
assistant: persistedAssistant,
|
|
318
323
|
assistantTranscriptOwned: true,
|