@skaile/workspaces 3.16.2 → 3.18.0
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 +69 -0
- package/dist/app-sdk/index.js +1 -1
- package/dist/{asset-feeds-FQT6GH24.js → asset-feeds-LT4GI576.js} +7 -7
- package/dist/{asset-feeds-FQT6GH24.js.map → asset-feeds-LT4GI576.js.map} +1 -1
- package/dist/asset-manager/catalog-deployer.js +3 -3
- package/dist/asset-manager/index.js +6 -6
- package/dist/asset-manager/installer.js +4 -4
- package/dist/bridge/codex-app-server.d.ts +2 -0
- package/dist/bridge/codex-app-server.js +5 -0
- package/dist/bridge/codex-app-server.js.map +1 -0
- package/dist/bridge/drivers/claude-sdk.js +5 -5
- package/dist/bridge/drivers/codex.js +1137 -4
- package/dist/bridge/drivers/codex.js.map +1 -1
- package/dist/bridge/drivers/continue.js +2 -2
- package/dist/bridge/drivers/echo.js +2 -2
- package/dist/bridge/drivers/gemini.js +3 -3
- package/dist/bridge/drivers/goose.js +2 -2
- package/dist/bridge/drivers/omp.js +1 -1
- package/dist/bridge/drivers/opencode.js +2 -2
- package/dist/bridge/drivers/qwen.js +3 -3
- package/dist/bridge/index.js +7 -6
- package/dist/bridge/src/capability-dispatch.d.ts +1 -1
- package/dist/bridge/src/capability-dispatch.d.ts.map +1 -1
- package/dist/bridge/src/codex/credentials.d.ts +12 -0
- package/dist/bridge/src/codex/credentials.d.ts.map +1 -0
- package/dist/bridge/src/codex/inputs.d.ts +8 -0
- package/dist/bridge/src/codex/inputs.d.ts.map +1 -0
- package/dist/bridge/src/codex/instance-state.d.ts +16 -0
- package/dist/bridge/src/codex/instance-state.d.ts.map +1 -0
- package/dist/bridge/src/codex/managed-driver.d.ts +71 -0
- package/dist/bridge/src/codex/managed-driver.d.ts.map +1 -0
- package/dist/bridge/src/codex/native-approvals.d.ts +22 -0
- package/dist/bridge/src/codex/native-approvals.d.ts.map +1 -0
- package/dist/bridge/src/codex/native-policy.d.ts +38 -0
- package/dist/bridge/src/codex/native-policy.d.ts.map +1 -0
- package/dist/bridge/src/codex/native-protocol.generated.d.ts +1411 -0
- package/dist/bridge/src/codex/native-protocol.generated.d.ts.map +1 -0
- package/dist/bridge/src/codex/native-state.d.ts +7 -0
- package/dist/bridge/src/codex/native-state.d.ts.map +1 -0
- package/dist/bridge/src/codex/questions.d.ts +19 -0
- package/dist/bridge/src/codex/questions.d.ts.map +1 -0
- package/dist/bridge/src/codex-app-server.d.ts +60 -0
- package/dist/bridge/src/codex-app-server.d.ts.map +1 -0
- package/dist/bridge/src/drivers/codex.d.ts +9 -1
- package/dist/bridge/src/drivers/codex.d.ts.map +1 -1
- package/dist/bridge/src/index.d.ts +1 -0
- package/dist/bridge/src/index.d.ts.map +1 -1
- package/dist/bridge/src/normalizer.d.ts.map +1 -1
- package/dist/bridge/src/runtime.d.ts +3 -2
- package/dist/bridge/src/runtime.d.ts.map +1 -1
- package/dist/bridge/src/types.d.ts +77 -4
- package/dist/bridge/src/types.d.ts.map +1 -1
- package/dist/{chunk-2KBA5OO2.js → chunk-23L3P7IB.js} +4 -4
- package/dist/{chunk-2KBA5OO2.js.map → chunk-23L3P7IB.js.map} +1 -1
- package/dist/{chunk-NC5YXVZF.js → chunk-2FDKUKDE.js} +3 -3
- package/dist/{chunk-NC5YXVZF.js.map → chunk-2FDKUKDE.js.map} +1 -1
- package/dist/{chunk-GRVSSAAQ.js → chunk-2IROGAS7.js} +2 -2
- package/dist/{chunk-GRVSSAAQ.js.map → chunk-2IROGAS7.js.map} +1 -1
- package/dist/{chunk-BUKPS4YL.js → chunk-4JDCKPTM.js} +2 -2
- package/dist/{chunk-BUKPS4YL.js.map → chunk-4JDCKPTM.js.map} +1 -1
- package/dist/{chunk-5KSIVLOX.js → chunk-5T7Y3AZU.js} +3 -3
- package/dist/{chunk-5KSIVLOX.js.map → chunk-5T7Y3AZU.js.map} +1 -1
- package/dist/{chunk-SWUVULBU.js → chunk-67KB2A77.js} +7 -7
- package/dist/{chunk-SWUVULBU.js.map → chunk-67KB2A77.js.map} +1 -1
- package/dist/{chunk-RRVQAE5D.js → chunk-7ZO3MUP3.js} +6 -5
- package/dist/chunk-7ZO3MUP3.js.map +1 -0
- package/dist/{chunk-EO7IJY3Q.js → chunk-AI2PZX4H.js} +3 -3
- package/dist/{chunk-EO7IJY3Q.js.map → chunk-AI2PZX4H.js.map} +1 -1
- package/dist/{chunk-CMGYUF5P.js → chunk-BJI7IGZF.js} +7 -7
- package/dist/{chunk-CMGYUF5P.js.map → chunk-BJI7IGZF.js.map} +1 -1
- package/dist/{chunk-HY6CIKFH.js → chunk-CP62G5PK.js} +8 -7
- package/dist/chunk-CP62G5PK.js.map +1 -0
- package/dist/{chunk-PBRMY64S.js → chunk-CQY7XKRV.js} +30 -9
- package/dist/chunk-CQY7XKRV.js.map +1 -0
- package/dist/{chunk-OIDFK7SG.js → chunk-DCG4UWJM.js} +3 -3
- package/dist/{chunk-OIDFK7SG.js.map → chunk-DCG4UWJM.js.map} +1 -1
- package/dist/{chunk-VT4MPCRK.js → chunk-FBD5SOIH.js} +3 -3
- package/dist/{chunk-VT4MPCRK.js.map → chunk-FBD5SOIH.js.map} +1 -1
- package/dist/{chunk-DUMD4AA2.js → chunk-FYVDBIZV.js} +6 -6
- package/dist/{chunk-DUMD4AA2.js.map → chunk-FYVDBIZV.js.map} +1 -1
- package/dist/{chunk-RTM4SFLA.js → chunk-GMKNGIIB.js} +7 -4
- package/dist/chunk-GMKNGIIB.js.map +1 -0
- package/dist/{chunk-NIQPSQFN.js → chunk-GMMACS5W.js} +4 -4
- package/dist/{chunk-NIQPSQFN.js.map → chunk-GMMACS5W.js.map} +1 -1
- package/dist/{chunk-QFYEDM63.js → chunk-GRA4BWCV.js} +5 -5
- package/dist/{chunk-QFYEDM63.js.map → chunk-GRA4BWCV.js.map} +1 -1
- package/dist/{chunk-CIPK5JMJ.js → chunk-H662SXKS.js} +4 -4
- package/dist/{chunk-CIPK5JMJ.js.map → chunk-H662SXKS.js.map} +1 -1
- package/dist/{chunk-4W2OOVN5.js → chunk-IST2V7RU.js} +3 -3
- package/dist/{chunk-4W2OOVN5.js.map → chunk-IST2V7RU.js.map} +1 -1
- package/dist/{chunk-L2UE4YJO.js → chunk-IT42ZT3T.js} +9 -9
- package/dist/{chunk-L2UE4YJO.js.map → chunk-IT42ZT3T.js.map} +1 -1
- package/dist/{chunk-FRER2PEN.js → chunk-J7VSUGGH.js} +16 -3
- package/dist/chunk-J7VSUGGH.js.map +1 -0
- package/dist/{chunk-ZNCZ7RUO.js → chunk-JVFZ5R7L.js} +3 -3
- package/dist/{chunk-ZNCZ7RUO.js.map → chunk-JVFZ5R7L.js.map} +1 -1
- package/dist/chunk-LOO5CB5M.js +12 -0
- package/dist/chunk-LOO5CB5M.js.map +1 -0
- package/dist/{chunk-AL6BU3SP.js → chunk-OJ4N4T4H.js} +12 -4
- package/dist/chunk-OJ4N4T4H.js.map +1 -0
- package/dist/chunk-QEZPFJX3.js +254 -0
- package/dist/chunk-QEZPFJX3.js.map +1 -0
- package/dist/{chunk-C4AP3XZB.js → chunk-QG2ZXU5W.js} +4 -4
- package/dist/{chunk-C4AP3XZB.js.map → chunk-QG2ZXU5W.js.map} +1 -1
- package/dist/{chunk-I3COVVFB.js → chunk-RJOGAEIY.js} +1024 -204
- package/dist/chunk-RJOGAEIY.js.map +1 -0
- package/dist/{chunk-OZ7UNMTK.js → chunk-RPVTPVGX.js} +7 -5
- package/dist/chunk-RPVTPVGX.js.map +1 -0
- package/dist/{chunk-S4UA244H.js → chunk-S635WMXH.js} +4 -4
- package/dist/{chunk-S4UA244H.js.map → chunk-S635WMXH.js.map} +1 -1
- package/dist/{chunk-BQMRUH7J.js → chunk-SFCVAEJ5.js} +4 -4
- package/dist/{chunk-BQMRUH7J.js.map → chunk-SFCVAEJ5.js.map} +1 -1
- package/dist/{chunk-EXT2LNST.js → chunk-SH7GXS7V.js} +125 -37
- package/dist/chunk-SH7GXS7V.js.map +1 -0
- package/dist/chunk-T3YLZQS4.js +336 -0
- package/dist/chunk-T3YLZQS4.js.map +1 -0
- package/dist/{chunk-X7FJ3ES3.js → chunk-X3LUB275.js} +4 -4
- package/dist/{chunk-X7FJ3ES3.js.map → chunk-X3LUB275.js.map} +1 -1
- package/dist/chunk-XKSGEYZC.js +244 -0
- package/dist/chunk-XKSGEYZC.js.map +1 -0
- package/dist/{chunk-SUPIVTLX.js → chunk-XL45U3C5.js} +3 -3
- package/dist/{chunk-SUPIVTLX.js.map → chunk-XL45U3C5.js.map} +1 -1
- package/dist/{chunk-AXEOG4B5.js → chunk-YCMHWNZA.js} +3 -3
- package/dist/{chunk-AXEOG4B5.js.map → chunk-YCMHWNZA.js.map} +1 -1
- package/dist/cli/index.js +75 -53
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/src/commands/serve.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/src/client.d.ts +3 -2
- package/dist/client/src/client.d.ts.map +1 -1
- package/dist/connectors/config.js +4 -4
- package/dist/connectors/index.js +12 -11
- package/dist/connectors/src/connector-prompt.d.ts +11 -0
- package/dist/connectors/src/connector-prompt.d.ts.map +1 -1
- package/dist/core/index.js +3 -3
- package/dist/core/runtime-assets.js +2 -2
- package/dist/core/src/workspace-config.d.ts.map +1 -1
- package/dist/core/workspace-config.js +1 -1
- package/dist/deploy/index.js +3 -3
- package/dist/{ensure-sources-4GBXK6UR.js → ensure-sources-4BQ6GMYG.js} +9 -9
- package/dist/{ensure-sources-4GBXK6UR.js.map → ensure-sources-4BQ6GMYG.js.map} +1 -1
- package/dist/factory-assets/connectors/box.js +12 -11
- package/dist/factory-assets/connectors/deploy.js +12 -11
- package/dist/factory-assets/connectors/devserver.js +12 -11
- package/dist/factory-assets/connectors/flow/CONNECTOR.md +4 -5
- package/dist/factory-assets/connectors/flow/adapter.d.ts +8 -0
- package/dist/factory-assets/connectors/flow/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/adapter.js +12 -11
- package/dist/factory-assets/connectors/flow/contract/index.d.ts +1 -1
- package/dist/factory-assets/connectors/flow/contract/index.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract/schema.d.ts +50 -17
- package/dist/factory-assets/connectors/flow/contract/schema.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract.js +3 -3
- package/dist/factory-assets/connectors/flow/engine/index.d.ts +1 -0
- package/dist/factory-assets/connectors/flow/engine/index.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine.js +5 -5
- package/dist/factory-assets/connectors/flow/run-flow.js +13 -12
- package/dist/factory-assets/connectors/flow/subprompt-runner.d.ts +10 -1
- package/dist/factory-assets/connectors/flow/subprompt-runner.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow.js +12 -11
- package/dist/factory-assets/connectors/git.js +12 -11
- package/dist/factory-assets/connectors/gmail.js +12 -11
- package/dist/factory-assets/connectors/googledrive.js +12 -11
- package/dist/factory-assets/connectors/local.js +12 -11
- package/dist/factory-assets/connectors/mattermost.js +12 -11
- package/dist/factory-assets/connectors/memory.js +12 -11
- package/dist/factory-assets/connectors/minio.js +12 -11
- package/dist/factory-assets/connectors/postgres.js +12 -11
- package/dist/factory-assets/connectors/s3.js +12 -11
- package/dist/factory-assets/connectors/sharepoint.js +12 -11
- package/dist/factory-assets/connectors/sqlite.js +12 -11
- package/dist/factory-assets/connectors/static-server.js +12 -11
- package/dist/factory-assets/connectors/tunnel.js +12 -11
- package/dist/factory-assets/connectors/webdav.js +12 -11
- package/dist/factory-assets/connectors/xstate-store.js +12 -11
- package/dist/factory-assets/connectors/xstate.js +12 -11
- package/dist/{flows-2SUFOVVO.js → flows-COBS26MK.js} +6 -6
- package/dist/{flows-2SUFOVVO.js.map → flows-COBS26MK.js.map} +1 -1
- package/dist/{helpers-26LW4IXY.js → helpers-4UJMSN6T.js} +9 -9
- package/dist/{helpers-26LW4IXY.js.map → helpers-4UJMSN6T.js.map} +1 -1
- package/dist/library/index.js +4 -4
- package/dist/library/install.js +3 -3
- package/dist/{library-publish-IYGHOPEK.js → library-publish-QHQDN4IC.js} +14 -14
- package/dist/{library-publish-IYGHOPEK.js.map → library-publish-QHQDN4IC.js.map} +1 -1
- package/dist/{open-library-HH5LST7Z.js → open-library-ZDM6DUKJ.js} +12 -12
- package/dist/{open-library-HH5LST7Z.js.map → open-library-ZDM6DUKJ.js.map} +1 -1
- package/dist/{paths-4APA2PKT.js → paths-CZPCCBYX.js} +6 -6
- package/dist/{paths-4APA2PKT.js.map → paths-CZPCCBYX.js.map} +1 -1
- package/dist/{plugin-store-JQAIKR3R.js → plugin-store-ZEIBSNTO.js} +5 -5
- package/dist/{plugin-store-JQAIKR3R.js.map → plugin-store-ZEIBSNTO.js.map} +1 -1
- package/dist/{project-U5QRUCBN.js → project-JJUAAI7N.js} +10 -10
- package/dist/{project-U5QRUCBN.js.map → project-JJUAAI7N.js.map} +1 -1
- package/dist/resolver/index.js +3 -3
- package/dist/runner/index.js +25 -16
- package/dist/runner/src/capability-registry.d.ts +4 -0
- package/dist/runner/src/capability-registry.d.ts.map +1 -1
- package/dist/runner/src/codex-init.d.ts +5 -0
- package/dist/runner/src/codex-init.d.ts.map +1 -0
- package/dist/runner/src/codex-resource-tools.d.ts +16 -0
- package/dist/runner/src/codex-resource-tools.d.ts.map +1 -0
- package/dist/runner/src/codex-tools.d.ts +12 -0
- package/dist/runner/src/codex-tools.d.ts.map +1 -0
- package/dist/runner/src/define-capability.d.ts +2 -0
- package/dist/runner/src/define-capability.d.ts.map +1 -1
- package/dist/runner/src/external-mcp.d.ts +8 -3
- package/dist/runner/src/external-mcp.d.ts.map +1 -1
- package/dist/runner/src/index.d.ts +2 -0
- package/dist/runner/src/index.d.ts.map +1 -1
- package/dist/runner/src/managed-codex-bootstrap.d.ts +54 -0
- package/dist/runner/src/managed-codex-bootstrap.d.ts.map +1 -0
- package/dist/runner/src/resources.d.ts +1 -0
- package/dist/runner/src/resources.d.ts.map +1 -1
- package/dist/runner/src/serve.d.ts +5 -0
- package/dist/runner/src/serve.d.ts.map +1 -1
- package/dist/runner/src/session-builder.d.ts +18 -1
- package/dist/runner/src/session-builder.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +6 -6
- package/dist/sdk/bridge.js +7 -6
- package/dist/sdk/client.js +1 -1
- package/dist/sdk/core.js +3 -3
- package/dist/sdk/flow.js +5 -5
- package/dist/sdk/index.js +26 -17
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/resolver.js +3 -3
- package/dist/sdk/runner.js +25 -16
- package/dist/sdk/session.js +2 -2
- package/dist/sdk/store.js +1 -1
- package/dist/sdk/types.js +1 -1
- package/dist/session/index.js +2 -2
- package/dist/{setup-XKTBHRWM.js → setup-PS6ZERZ5.js} +9 -8
- package/dist/setup-PS6ZERZ5.js.map +1 -0
- package/dist/{skills-Q7PJVB3W.js → skills-LG6IQSSN.js} +5 -5
- package/dist/{skills-Q7PJVB3W.js.map → skills-LG6IQSSN.js.map} +1 -1
- package/dist/{source-6PBVTACZ.js → source-QLDIZCI3.js} +13 -13
- package/dist/{source-6PBVTACZ.js.map → source-QLDIZCI3.js.map} +1 -1
- package/dist/{source-manifest-publish-VBVNW7KL.js → source-manifest-publish-J3AVCTLX.js} +15 -15
- package/dist/{source-manifest-publish-VBVNW7KL.js.map → source-manifest-publish-J3AVCTLX.js.map} +1 -1
- package/dist/store/index.js +1 -1
- package/dist/store/react.js +1 -1
- package/dist/store/react.js.map +1 -1
- package/dist/store/src/react.d.ts +1 -1
- package/dist/store/src/react.d.ts.map +1 -1
- package/dist/store/src/store.d.ts +2 -1
- package/dist/store/src/store.d.ts.map +1 -1
- package/dist/tui/index.js +25 -16
- package/dist/tui/index.js.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/src/events.d.ts +14 -1
- package/dist/types/src/events.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +3 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/lifecycle.d.ts +43 -0
- package/dist/types/src/lifecycle.d.ts.map +1 -1
- package/dist/types/src/protocol.d.ts +18 -0
- package/dist/types/src/protocol.d.ts.map +1 -1
- package/dist/types/src/runtime.d.ts +3 -1
- package/dist/types/src/runtime.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +7 -1
- package/dist/types/src/version.d.ts.map +1 -1
- package/dist/workspace-plugin/adapters/mcp.js +9 -255
- package/dist/workspace-plugin/adapters/mcp.js.map +1 -1
- package/dist/workspace-plugin/adapters/omp.js +7 -7
- package/dist/workspace-plugin/index.js +1 -1
- package/package.json +10 -1
- package/dist/chunk-AL6BU3SP.js.map +0 -1
- package/dist/chunk-EXT2LNST.js.map +0 -1
- package/dist/chunk-FRER2PEN.js.map +0 -1
- package/dist/chunk-HY6CIKFH.js.map +0 -1
- package/dist/chunk-I3COVVFB.js.map +0 -1
- package/dist/chunk-OZ7UNMTK.js.map +0 -1
- package/dist/chunk-PBRMY64S.js.map +0 -1
- package/dist/chunk-RRVQAE5D.js.map +0 -1
- package/dist/chunk-RTM4SFLA.js.map +0 -1
- package/dist/setup-XKTBHRWM.js.map +0 -1
|
@@ -1,14 +1,1108 @@
|
|
|
1
|
+
import { CodexAppServerClient } from '../../chunk-XKSGEYZC.js';
|
|
2
|
+
import { parseCodexCredential, prepareNativePolicy, prepareCodexInstance, codexInstanceEnvironment, activateNativeApprovalPolicy, parseCodexRefreshResult, isNativeApprovalPolicyActive, allowsNativeApprovalPath } from '../../chunk-T3YLZQS4.js';
|
|
3
|
+
import { CodexNativeStateUnavailable } from '../../chunk-LOO5CB5M.js';
|
|
1
4
|
import { fetchProviderModels } from '../../chunk-GURXUF2J.js';
|
|
2
5
|
import { classifyGenericErrorMessage } from '../../chunk-GAH4YRCI.js';
|
|
3
|
-
import { BUILTIN_DRIVER_CATALOG, AgentDriver, getBridgeLogger } from '../../chunk-
|
|
6
|
+
import { BUILTIN_DRIVER_CATALOG, AgentDriver, getBridgeLogger } from '../../chunk-J7VSUGGH.js';
|
|
4
7
|
import '../../chunk-6E6PKKAD.js';
|
|
5
8
|
import '../../chunk-FTG7CRFD.js';
|
|
6
9
|
import '../../chunk-W4IPN3DD.js';
|
|
7
10
|
import '../../chunk-NSBPE2FW.js';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
11
|
+
import { join, dirname, isAbsolute, relative } from 'path';
|
|
12
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync, realpathSync, statSync } from 'fs';
|
|
13
|
+
import { randomUUID } from 'crypto';
|
|
10
14
|
import * as z from 'zod';
|
|
11
15
|
|
|
16
|
+
var invalid = () => new Error("CODEX_INPUT_INVALID");
|
|
17
|
+
var inside = (root, path) => {
|
|
18
|
+
const rel = relative(root, path);
|
|
19
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
|
|
20
|
+
};
|
|
21
|
+
function codexPromptInput(input, cwd, skills) {
|
|
22
|
+
if (!input || typeof input.text !== "string" || input.text.length > 1024 * 1024 || input.attachments !== void 0 && !Array.isArray(input.attachments) || (input.attachments?.length ?? 0) > 32)
|
|
23
|
+
throw invalid();
|
|
24
|
+
const result = [
|
|
25
|
+
{ type: "text", text: input.text }
|
|
26
|
+
];
|
|
27
|
+
for (const attachment of input.attachments ?? []) {
|
|
28
|
+
try {
|
|
29
|
+
if (!attachment || typeof attachment.path !== "string") throw invalid();
|
|
30
|
+
const root = attachment.type === "skill" && skills ? realpathSync(skills.source) : realpathSync(cwd);
|
|
31
|
+
const path = realpathSync(
|
|
32
|
+
isAbsolute(attachment.path) ? attachment.path : join(root, attachment.path)
|
|
33
|
+
);
|
|
34
|
+
if (!inside(root, path) || !statSync(path).isFile()) throw invalid();
|
|
35
|
+
if (attachment.type === "image") result.push({ type: "localImage", path });
|
|
36
|
+
else if (attachment.type === "file" || attachment.type === "skill") {
|
|
37
|
+
if (!attachment.name || [...attachment.name].some((char) => char.charCodeAt(0) < 32))
|
|
38
|
+
throw invalid();
|
|
39
|
+
if (attachment.type === "skill") {
|
|
40
|
+
if (!skills) throw invalid();
|
|
41
|
+
const copied = realpathSync(join(skills.target, relative(root, path)));
|
|
42
|
+
if (!inside(realpathSync(skills.target), copied) || !statSync(copied).isFile())
|
|
43
|
+
throw invalid();
|
|
44
|
+
result.push({ type: "skill", name: attachment.name, path: copied });
|
|
45
|
+
} else result.push({ type: "mention", name: attachment.name, path });
|
|
46
|
+
} else throw invalid();
|
|
47
|
+
} catch {
|
|
48
|
+
throw invalid();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// bridge/src/codex/native-approvals.ts
|
|
55
|
+
function createCodexNativeApprovalHandlers(options) {
|
|
56
|
+
const active = (signal, threadId) => !signal.aborted && isNativeApprovalPolicyActive(options.getPolicy(), threadId);
|
|
57
|
+
const text = (value) => typeof value === "string" && value.length > 0 && value.length <= 8192;
|
|
58
|
+
const scoped = async (path, access, signal, threadId) => active(signal, threadId) && text(path) && allowsNativeApprovalPath(options.getPolicy(), path, access) && active(signal, threadId);
|
|
59
|
+
const permissions = async (value, signal, threadId) => {
|
|
60
|
+
if (value.network?.enabled || value.network != null && Object.keys(value.network).some((key) => key !== "enabled") || Object.keys(value).some((key) => !["network", "fileSystem"].includes(key)))
|
|
61
|
+
return false;
|
|
62
|
+
const fs = value.fileSystem;
|
|
63
|
+
if (!fs) return true;
|
|
64
|
+
if (fs.entries != null || fs.globScanMaxDepth != null || Object.keys(fs).some((key) => !["read", "write", "entries", "globScanMaxDepth"].includes(key)))
|
|
65
|
+
return false;
|
|
66
|
+
for (const access of ["read", "write"]) {
|
|
67
|
+
const paths = fs[access];
|
|
68
|
+
if (paths != null && (!Array.isArray(paths) || paths.length > 32)) return false;
|
|
69
|
+
for (const path of paths ?? [])
|
|
70
|
+
if (!await scoped(path, access, signal, threadId)) return false;
|
|
71
|
+
}
|
|
72
|
+
return active(signal, threadId);
|
|
73
|
+
};
|
|
74
|
+
const consent = async (identity, approval, signal) => {
|
|
75
|
+
if (!active(signal, identity.threadId) || approval.details.length > 64 || approval.details.some((detail) => detail.length > 8192) || approval.details.reduce((sum, detail) => sum + detail.length, 0) > 65536)
|
|
76
|
+
return false;
|
|
77
|
+
try {
|
|
78
|
+
return await options.questions.requestApproval(identity, approval, signal) && active(signal, identity.threadId);
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
"item/commandExecution/requestApproval": async (input, signal) => {
|
|
85
|
+
const params = structuredClone(input);
|
|
86
|
+
const allowed = async () => active(signal, params.threadId) && text(params.command) && (params.kind == null || params.kind === "command") && !params.environmentId && !params.networkApprovalContext && (params.availableDecisions == null || params.availableDecisions.includes("accept")) && await scoped(params.cwd, "read", signal, params.threadId) && (!params.additionalPermissions || await permissions(params.additionalPermissions, signal, params.threadId));
|
|
87
|
+
if (!await allowed()) return { decision: "decline" };
|
|
88
|
+
const details = [`Command: ${params.command}`, `Directory: ${params.cwd}`];
|
|
89
|
+
for (const access of ["read", "write"])
|
|
90
|
+
for (const path of params.additionalPermissions?.fileSystem?.[access] ?? [])
|
|
91
|
+
details.push(`${access}: ${path}`);
|
|
92
|
+
const accepted = await consent(params, { kind: "command", details }, signal);
|
|
93
|
+
return { decision: accepted && await allowed() ? "accept" : "decline" };
|
|
94
|
+
},
|
|
95
|
+
"item/fileChange/requestApproval": async (input, signal) => {
|
|
96
|
+
const params = structuredClone(input);
|
|
97
|
+
if (!active(signal, params.threadId)) return { decision: "decline" };
|
|
98
|
+
const original = options.getFileChanges(params);
|
|
99
|
+
if (!original?.length || original.length > 16) return { decision: "decline" };
|
|
100
|
+
const changes = structuredClone(original);
|
|
101
|
+
const snapshot = JSON.stringify(changes);
|
|
102
|
+
const allowed = async () => {
|
|
103
|
+
if (!active(signal, params.threadId) || snapshot !== JSON.stringify(options.getFileChanges(params)))
|
|
104
|
+
return false;
|
|
105
|
+
if (params.grantRoot != null && !await scoped(params.grantRoot, "write", signal, params.threadId))
|
|
106
|
+
return false;
|
|
107
|
+
for (const change of changes) {
|
|
108
|
+
if (!text(change.diff) || !await scoped(change.path, "write", signal, params.threadId))
|
|
109
|
+
return false;
|
|
110
|
+
if (!["add", "delete", "update"].includes(change.kind.type)) return false;
|
|
111
|
+
if (change.kind.type === "update" && change.kind.move_path != null && !await scoped(change.kind.move_path, "write", signal, params.threadId))
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
return active(signal, params.threadId);
|
|
115
|
+
};
|
|
116
|
+
if (!await allowed()) return { decision: "decline" };
|
|
117
|
+
const details = changes.flatMap((change) => [
|
|
118
|
+
`${change.kind.type}: ${change.path}${change.kind.type === "update" && change.kind.move_path ? ` \u2192 ${change.kind.move_path}` : ""}`,
|
|
119
|
+
change.diff
|
|
120
|
+
]);
|
|
121
|
+
const accepted = await consent(params, { kind: "file", details }, signal);
|
|
122
|
+
return { decision: accepted && await allowed() ? "accept" : "decline" };
|
|
123
|
+
},
|
|
124
|
+
"item/permissions/requestApproval": async (input, signal) => {
|
|
125
|
+
const params = structuredClone(input);
|
|
126
|
+
const allowed = async () => active(signal, params.threadId) && !params.environmentId && await scoped(params.cwd, "read", signal, params.threadId) && await permissions(params.permissions, signal, params.threadId);
|
|
127
|
+
if (!await allowed()) return { permissions: {}, scope: "turn" };
|
|
128
|
+
const details = [`Directory: ${params.cwd}`];
|
|
129
|
+
for (const access of ["read", "write"])
|
|
130
|
+
for (const path of params.permissions.fileSystem?.[access] ?? [])
|
|
131
|
+
details.push(`${access}: ${path}`);
|
|
132
|
+
if (details.length === 1) return { permissions: {}, scope: "turn" };
|
|
133
|
+
const accepted = await consent(params, { kind: "permissions", details }, signal);
|
|
134
|
+
return {
|
|
135
|
+
permissions: accepted && await allowed() ? params.permissions : {},
|
|
136
|
+
scope: "turn"
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
var CodexQuestions = class {
|
|
142
|
+
constructor(emit) {
|
|
143
|
+
this.emit = emit;
|
|
144
|
+
}
|
|
145
|
+
emit;
|
|
146
|
+
pending;
|
|
147
|
+
request = async (params, signal) => this.park(params, signal);
|
|
148
|
+
/** Park one explicit native consent request in the same slot as ordinary questions. */
|
|
149
|
+
async requestApproval(identity, approval, signal) {
|
|
150
|
+
const result = await this.park(
|
|
151
|
+
{
|
|
152
|
+
...identity,
|
|
153
|
+
isBlocking: true,
|
|
154
|
+
questions: [
|
|
155
|
+
{
|
|
156
|
+
id: "native-approval",
|
|
157
|
+
header: "Approval",
|
|
158
|
+
question: approval.kind === "permissions" ? "Allow these permissions for the current turn?" : "Allow this native action once?",
|
|
159
|
+
options: [
|
|
160
|
+
{
|
|
161
|
+
label: "Approve once",
|
|
162
|
+
description: approval.kind === "permissions" ? "Allow for the current turn." : "Allow only this request."
|
|
163
|
+
},
|
|
164
|
+
{ label: "Deny", description: "Do not allow this request." }
|
|
165
|
+
]
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
signal,
|
|
170
|
+
approval
|
|
171
|
+
);
|
|
172
|
+
return result.answers["native-approval"]?.answers[0] === "Approve once";
|
|
173
|
+
}
|
|
174
|
+
async park(params, signal, approval) {
|
|
175
|
+
signal.throwIfAborted();
|
|
176
|
+
if (this.pending || !params.isBlocking || !Array.isArray(params.questions) || params.questions.length === 0 || params.questions.length > 16 || params.questions.some((q) => !q.id || !q.question || q.isSecret) || new Set(params.questions.map((q) => q.id)).size !== params.questions.length || new Set(params.questions.map((q) => q.question)).size !== params.questions.length)
|
|
177
|
+
throw new Error("CODEX_INPUT_UNSUPPORTED");
|
|
178
|
+
const id = randomUUID();
|
|
179
|
+
return new Promise((resolve, reject) => {
|
|
180
|
+
const abort = () => finish();
|
|
181
|
+
const finish = (result) => {
|
|
182
|
+
if (this.pending?.id !== id) return;
|
|
183
|
+
this.pending = void 0;
|
|
184
|
+
signal.removeEventListener("abort", abort);
|
|
185
|
+
if (result && !signal.aborted) resolve(result);
|
|
186
|
+
else reject(new Error("CODEX_INPUT_CANCELLED"));
|
|
187
|
+
};
|
|
188
|
+
this.pending = {
|
|
189
|
+
id,
|
|
190
|
+
questions: params.questions,
|
|
191
|
+
answers: /* @__PURE__ */ Object.create(null),
|
|
192
|
+
finish,
|
|
193
|
+
approval
|
|
194
|
+
};
|
|
195
|
+
const input = {
|
|
196
|
+
requestId: id,
|
|
197
|
+
questions: params.questions,
|
|
198
|
+
...approval ? { approval } : {}
|
|
199
|
+
};
|
|
200
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
201
|
+
if (signal.aborted) {
|
|
202
|
+
abort();
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
this.emit({
|
|
206
|
+
type: "message_start",
|
|
207
|
+
message: {
|
|
208
|
+
role: "assistant",
|
|
209
|
+
content: [
|
|
210
|
+
{
|
|
211
|
+
type: "tool_use",
|
|
212
|
+
name: "AskUserQuestion",
|
|
213
|
+
id: params.itemId,
|
|
214
|
+
input
|
|
215
|
+
}
|
|
216
|
+
]
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
this.emit({
|
|
220
|
+
type: "tool_call",
|
|
221
|
+
name: "AskUserQuestion",
|
|
222
|
+
tool: { name: "AskUserQuestion" },
|
|
223
|
+
toolCallId: params.itemId,
|
|
224
|
+
input
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
get active() {
|
|
229
|
+
return Boolean(this.pending);
|
|
230
|
+
}
|
|
231
|
+
answer(answer, question, requestId) {
|
|
232
|
+
const pending = this.pending;
|
|
233
|
+
if (!pending || pending.id !== requestId || typeof answer !== "string") return false;
|
|
234
|
+
if (pending.approval && (question !== pending.questions[0]?.question || !["Approve once", "Deny"].includes(answer)))
|
|
235
|
+
return false;
|
|
236
|
+
const q = question === void 0 ? pending.questions.find((entry) => !Object.hasOwn(pending.answers, entry.id)) : pending.questions.find((entry) => entry.question === question);
|
|
237
|
+
if (!q || Object.hasOwn(pending.answers, q.id)) return false;
|
|
238
|
+
pending.answers[q.id] = { answers: [answer] };
|
|
239
|
+
if (pending.questions.every((entry) => Object.hasOwn(pending.answers, entry.id)))
|
|
240
|
+
pending.finish({ answers: pending.answers });
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// bridge/src/codex/managed-driver.ts
|
|
246
|
+
var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
247
|
+
var failure = (code = "CODEX_RUNTIME_UNSUPPORTED") => new Error(code);
|
|
248
|
+
var nativeUsage = (value) => ({
|
|
249
|
+
...typeof value.inputTokens === "number" ? { inputTokens: value.inputTokens } : {},
|
|
250
|
+
...typeof value.outputTokens === "number" ? { outputTokens: value.outputTokens } : {},
|
|
251
|
+
...typeof value.cachedInputTokens === "number" ? { cacheReadTokens: value.cachedInputTokens } : {},
|
|
252
|
+
...typeof value.reasoningOutputTokens === "number" ? { reasoningTokens: value.reasoningOutputTokens } : {},
|
|
253
|
+
...typeof value.totalTokens === "number" ? { totalTokens: value.totalTokens } : {}
|
|
254
|
+
});
|
|
255
|
+
var subtractUsage = (total, baseline) => {
|
|
256
|
+
const delta = (value, before) => value === void 0 ? void 0 : Math.max(0, value - (before ?? 0));
|
|
257
|
+
return {
|
|
258
|
+
...delta(total.inputTokens, baseline?.inputTokens) !== void 0 ? { inputTokens: delta(total.inputTokens, baseline?.inputTokens) } : {},
|
|
259
|
+
...delta(total.outputTokens, baseline?.outputTokens) !== void 0 ? { outputTokens: delta(total.outputTokens, baseline?.outputTokens) } : {},
|
|
260
|
+
...delta(total.cacheReadTokens, baseline?.cacheReadTokens) !== void 0 ? {
|
|
261
|
+
cacheReadTokens: delta(total.cacheReadTokens, baseline?.cacheReadTokens)
|
|
262
|
+
} : {},
|
|
263
|
+
...delta(total.reasoningTokens, baseline?.reasoningTokens) !== void 0 ? {
|
|
264
|
+
reasoningTokens: delta(total.reasoningTokens, baseline?.reasoningTokens)
|
|
265
|
+
} : {},
|
|
266
|
+
...delta(total.totalTokens, baseline?.totalTokens) !== void 0 ? { totalTokens: delta(total.totalTokens, baseline?.totalTokens) } : {}
|
|
267
|
+
};
|
|
268
|
+
};
|
|
269
|
+
var addUsage = (left, right) => {
|
|
270
|
+
const result = {};
|
|
271
|
+
for (const key of [
|
|
272
|
+
"inputTokens",
|
|
273
|
+
"outputTokens",
|
|
274
|
+
"cacheReadTokens",
|
|
275
|
+
"reasoningTokens",
|
|
276
|
+
"totalTokens"
|
|
277
|
+
]) {
|
|
278
|
+
if (left?.[key] !== void 0 || right[key] !== void 0)
|
|
279
|
+
result[key] = (left?.[key] ?? 0) + (right[key] ?? 0);
|
|
280
|
+
}
|
|
281
|
+
return result;
|
|
282
|
+
};
|
|
283
|
+
var limit = (value, fallback) => {
|
|
284
|
+
const result = value ?? fallback;
|
|
285
|
+
if (!Number.isSafeInteger(result) || result <= 0 || result > 18e5)
|
|
286
|
+
throw failure("AI_PROFILE_INVALID");
|
|
287
|
+
return result;
|
|
288
|
+
};
|
|
289
|
+
async function bounded(work, signal, ms) {
|
|
290
|
+
let timer;
|
|
291
|
+
let cancel = () => {
|
|
292
|
+
};
|
|
293
|
+
try {
|
|
294
|
+
return await Promise.race([
|
|
295
|
+
work,
|
|
296
|
+
new Promise((_, reject) => {
|
|
297
|
+
cancel = () => reject(failure());
|
|
298
|
+
if (signal.aborted) cancel();
|
|
299
|
+
else signal.addEventListener("abort", cancel, { once: true });
|
|
300
|
+
timer = setTimeout(() => reject(failure()), ms);
|
|
301
|
+
})
|
|
302
|
+
]);
|
|
303
|
+
} finally {
|
|
304
|
+
clearTimeout(timer);
|
|
305
|
+
signal.removeEventListener("abort", cancel);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
var ManagedCodexDriver = class extends AgentDriver {
|
|
309
|
+
constructor(config) {
|
|
310
|
+
super();
|
|
311
|
+
this.config = config;
|
|
312
|
+
}
|
|
313
|
+
config;
|
|
314
|
+
driverInfo = {
|
|
315
|
+
id: "codex",
|
|
316
|
+
name: "OpenAI Codex",
|
|
317
|
+
modelAgnostic: false,
|
|
318
|
+
supportsInBandAbort: true
|
|
319
|
+
};
|
|
320
|
+
client;
|
|
321
|
+
connecting;
|
|
322
|
+
options;
|
|
323
|
+
credential;
|
|
324
|
+
nativeApprovalPolicy;
|
|
325
|
+
instance;
|
|
326
|
+
tools;
|
|
327
|
+
mcpStartup = "unknown";
|
|
328
|
+
pendingMcpStartup;
|
|
329
|
+
mcpDiagnostics = {
|
|
330
|
+
startupBeforeTurn: "unknown",
|
|
331
|
+
startupAtEnd: "unknown",
|
|
332
|
+
toolsListBeforeTurn: 0,
|
|
333
|
+
toolsListTotal: 0,
|
|
334
|
+
toolsCallTotal: 0,
|
|
335
|
+
nativeMcpToolItemCount: 0
|
|
336
|
+
};
|
|
337
|
+
controller = new AbortController();
|
|
338
|
+
generation = 0;
|
|
339
|
+
killed = false;
|
|
340
|
+
broken = false;
|
|
341
|
+
cleanupUncertain = false;
|
|
342
|
+
unexpectedClose = false;
|
|
343
|
+
starting;
|
|
344
|
+
closing = Promise.resolve();
|
|
345
|
+
threadId;
|
|
346
|
+
lastThreadId;
|
|
347
|
+
resetting;
|
|
348
|
+
pendingPatch;
|
|
349
|
+
active;
|
|
350
|
+
threadUsageTotal;
|
|
351
|
+
resumeUsageTurnIds = /* @__PURE__ */ new Set();
|
|
352
|
+
queue = Promise.resolve();
|
|
353
|
+
queued = 0;
|
|
354
|
+
admittedTurns = 0;
|
|
355
|
+
loginCompleted;
|
|
356
|
+
startupRefreshUsed = false;
|
|
357
|
+
startupAuthAttempted = false;
|
|
358
|
+
questions = new CodexQuestions((event) => this.event(event));
|
|
359
|
+
get acceptsReplyAsPrompt() {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
hasPendingQuestion() {
|
|
363
|
+
return this.questions.active;
|
|
364
|
+
}
|
|
365
|
+
answerQuestion(answer, question, requestId) {
|
|
366
|
+
return this.questions.answer(answer, question, requestId);
|
|
367
|
+
}
|
|
368
|
+
get runtimeSessionId() {
|
|
369
|
+
return this.threadId ?? this.lastThreadId;
|
|
370
|
+
}
|
|
371
|
+
get isRunning() {
|
|
372
|
+
return Boolean(this.client && this.threadId && !this.killed && !this.broken);
|
|
373
|
+
}
|
|
374
|
+
getModel() {
|
|
375
|
+
return this.config.model;
|
|
376
|
+
}
|
|
377
|
+
async start() {
|
|
378
|
+
if (this.killed || this.broken) throw failure();
|
|
379
|
+
if (this.threadId && this.client) return;
|
|
380
|
+
if (this.starting) return this.starting;
|
|
381
|
+
this.starting = this.initialize().catch(async (error) => {
|
|
382
|
+
this.broken = true;
|
|
383
|
+
await this.closeGeneration();
|
|
384
|
+
if (error instanceof CodexNativeStateUnavailable && (this.cleanupUncertain || this.unexpectedClose || this.killed || this.startupAuthAttempted))
|
|
385
|
+
throw failure();
|
|
386
|
+
const safe = error instanceof Error && /^(AI_PROFILE_INVALID|CODEX_[A-Z_]+)$/.test(error.message) ? error : failure();
|
|
387
|
+
throw safe;
|
|
388
|
+
}).finally(() => {
|
|
389
|
+
this.starting = void 0;
|
|
390
|
+
});
|
|
391
|
+
return this.starting;
|
|
392
|
+
}
|
|
393
|
+
async initialize() {
|
|
394
|
+
await this.closing;
|
|
395
|
+
if (this.killed) throw failure();
|
|
396
|
+
const options = this.config.driverOptions?.codex?.managedRuntime;
|
|
397
|
+
const tuning = this.config.driverOptions?.codex;
|
|
398
|
+
if (tuning?.authSource !== "managed" || !options || typeof options.prepareTools !== "function" || !isAbsolute(options.executable) || !this.config.model || !tuning.reasoningEffort || !tuning.approvalPolicy || tuning.approvalPolicy === "on-failure" || !tuning.sandboxMode || tuning.sandboxMode === "danger-full-access" || this.config.provider && this.config.provider !== "openai" || this.config.apiKeys?.openai || this.config.env?.OPENAI_API_KEY || options.nativeTurnBudget !== void 0 && options.nativeTurnBudget !== 1)
|
|
399
|
+
throw failure("AI_PROFILE_INVALID");
|
|
400
|
+
this.options = options;
|
|
401
|
+
const timeout = limit(options.requestTimeoutMs, 15e3);
|
|
402
|
+
limit(options.turnTimeoutMs, 3e5);
|
|
403
|
+
try {
|
|
404
|
+
this.credential = parseCodexCredential(this.credential ?? options.credential);
|
|
405
|
+
} catch {
|
|
406
|
+
throw failure("CODEX_AUTH_UNAVAILABLE");
|
|
407
|
+
}
|
|
408
|
+
if (this.credential.configId !== this.config.aiProviderConfigId)
|
|
409
|
+
throw failure("AI_PROFILE_INVALID");
|
|
410
|
+
const privatePolicy = prepareNativePolicy({
|
|
411
|
+
cwd: this.config.cwd,
|
|
412
|
+
privateRoot: options.privateRoot,
|
|
413
|
+
privateDirectory: options.privateDirectory,
|
|
414
|
+
sandboxMode: tuning.sandboxMode
|
|
415
|
+
});
|
|
416
|
+
this.instance = prepareCodexInstance(this.config, options, this.credential);
|
|
417
|
+
const generation = ++this.generation;
|
|
418
|
+
this.controller = new AbortController();
|
|
419
|
+
this.startupRefreshUsed = false;
|
|
420
|
+
this.startupAuthAttempted = false;
|
|
421
|
+
const handlers = {
|
|
422
|
+
"account/chatgptAuthTokens/refresh": async (params, signal) => {
|
|
423
|
+
const active = this.active;
|
|
424
|
+
if (!active) this.startupAuthAttempted = true;
|
|
425
|
+
const cancellation = AbortSignal.any([
|
|
426
|
+
signal,
|
|
427
|
+
this.controller.signal,
|
|
428
|
+
...active ? [active.controller.signal] : []
|
|
429
|
+
]);
|
|
430
|
+
try {
|
|
431
|
+
return await this.refresh(params, cancellation);
|
|
432
|
+
} catch {
|
|
433
|
+
if (active && this.active === active) active.failureCode = "CODEX_AUTH_UNAVAILABLE";
|
|
434
|
+
throw failure("CODEX_AUTH_UNAVAILABLE");
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
const authMode = this.credential.kind === "codex-subscription" ? "chatgpt" : "api";
|
|
439
|
+
const nativePolicy = {
|
|
440
|
+
cli_auth_credentials_store: "ephemeral",
|
|
441
|
+
forced_login_method: authMode,
|
|
442
|
+
model_provider: "openai",
|
|
443
|
+
model: this.config.model,
|
|
444
|
+
model_instructions_file: this.instance.instructionFile,
|
|
445
|
+
shell_environment_policy: {
|
|
446
|
+
inherit: "none",
|
|
447
|
+
set: { PATH: options.environment.PATH ?? "/usr/bin:/bin" }
|
|
448
|
+
},
|
|
449
|
+
mcp_servers: {}
|
|
450
|
+
};
|
|
451
|
+
const overrides = Object.entries({
|
|
452
|
+
cli_auth_credentials_store: "ephemeral",
|
|
453
|
+
forced_login_method: authMode,
|
|
454
|
+
model_provider: "openai",
|
|
455
|
+
model: this.config.model,
|
|
456
|
+
model_instructions_file: this.instance.instructionFile,
|
|
457
|
+
"shell_environment_policy.inherit": "none",
|
|
458
|
+
"shell_environment_policy.set.PATH": options.environment.PATH ?? "/usr/bin:/bin"
|
|
459
|
+
}).flatMap(([key, value]) => ["-c", `${key}=${JSON.stringify(value)}`]);
|
|
460
|
+
overrides.push("-c", "mcp_servers={}");
|
|
461
|
+
const connecting = CodexAppServerClient.connect(
|
|
462
|
+
{
|
|
463
|
+
command: options.executable,
|
|
464
|
+
args: [
|
|
465
|
+
...options.executableArgs ?? [],
|
|
466
|
+
...overrides,
|
|
467
|
+
...privatePolicy.cliArgs,
|
|
468
|
+
"app-server",
|
|
469
|
+
"--stdio"
|
|
470
|
+
],
|
|
471
|
+
cwd: this.instance.root,
|
|
472
|
+
env: codexInstanceEnvironment(options.environment, this.instance.home),
|
|
473
|
+
clientInfo: { name: "skaile-managed-codex", version: "1" },
|
|
474
|
+
processGroup: true,
|
|
475
|
+
requestTimeoutMs: timeout,
|
|
476
|
+
serverRequestTimeoutMs: Math.min(limit(options.interactionTimeoutMs, 3e5), 3e5),
|
|
477
|
+
onServerRequest: handlers,
|
|
478
|
+
onNotification: (method, params) => {
|
|
479
|
+
if (generation === this.generation) this.notification(method, params);
|
|
480
|
+
},
|
|
481
|
+
onClose: () => {
|
|
482
|
+
if (generation !== this.generation) return;
|
|
483
|
+
this.unexpectedClose = true;
|
|
484
|
+
this.broken = true;
|
|
485
|
+
this.loginCompleted?.reject(failure());
|
|
486
|
+
this.finishTurn("CODEX_RUNTIME_UNSUPPORTED");
|
|
487
|
+
void this.closeGeneration();
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
this.controller.signal
|
|
491
|
+
);
|
|
492
|
+
this.connecting = connecting;
|
|
493
|
+
try {
|
|
494
|
+
this.client = await connecting;
|
|
495
|
+
} finally {
|
|
496
|
+
if (this.connecting === connecting) this.connecting = void 0;
|
|
497
|
+
}
|
|
498
|
+
if (generation !== this.generation || this.killed) throw failure();
|
|
499
|
+
await this.authenticate(timeout);
|
|
500
|
+
const toolsPromise = options.prepareTools(this.controller.signal).then(async (tools) => {
|
|
501
|
+
if (generation !== this.generation || this.controller.signal.aborted) {
|
|
502
|
+
await tools.dispose().catch(() => {
|
|
503
|
+
});
|
|
504
|
+
throw failure();
|
|
505
|
+
}
|
|
506
|
+
return tools;
|
|
507
|
+
});
|
|
508
|
+
try {
|
|
509
|
+
this.tools = await bounded(toolsPromise, this.controller.signal, timeout);
|
|
510
|
+
} catch {
|
|
511
|
+
throw failure("CODEX_REQUIRED_TOOL_UNAVAILABLE");
|
|
512
|
+
}
|
|
513
|
+
if (!this.tools || !record(this.tools.mcpServers) || typeof this.tools.dispose !== "function" || Object.keys(this.tools.mcpServers).length === 0 && Object.keys(this.tools.onServerRequest ?? {}).length === 0)
|
|
514
|
+
throw failure("CODEX_REQUIRED_TOOL_UNAVAILABLE");
|
|
515
|
+
if (this.tools.onServerRequest?.["item/tool/requestUserInput"])
|
|
516
|
+
throw failure("AI_PROFILE_INVALID");
|
|
517
|
+
const nativeApprovals = createCodexNativeApprovalHandlers({
|
|
518
|
+
questions: this.questions,
|
|
519
|
+
getPolicy: () => tuning.approvalPolicy === "never" ? void 0 : this.nativeApprovalPolicy,
|
|
520
|
+
getFileChanges: ({ itemId, threadId, turnId }) => {
|
|
521
|
+
const active = this.active;
|
|
522
|
+
if (!active || active.controller.signal.aborted || threadId !== this.threadId || turnId !== active.id)
|
|
523
|
+
return void 0;
|
|
524
|
+
const snapshot = active.fileChanges.get(itemId);
|
|
525
|
+
return snapshot ? JSON.parse(snapshot) : void 0;
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
for (const [method, handler] of Object.entries({
|
|
529
|
+
...this.tools.onServerRequest,
|
|
530
|
+
...nativeApprovals,
|
|
531
|
+
"item/tool/requestUserInput": this.questions.request
|
|
532
|
+
})) {
|
|
533
|
+
if (method === "account/chatgptAuthTokens/refresh" || !handler)
|
|
534
|
+
throw failure("AI_PROFILE_INVALID");
|
|
535
|
+
handlers[method] = async (params, signal) => {
|
|
536
|
+
const active = this.active;
|
|
537
|
+
if (generation !== this.generation || !active || !record(params)) throw failure();
|
|
538
|
+
await active.ready;
|
|
539
|
+
if (generation !== this.generation || this.active !== active || params.threadId !== this.threadId || params.turnId !== active.id)
|
|
540
|
+
throw failure();
|
|
541
|
+
const cancellation = AbortSignal.any([signal, active.controller.signal]);
|
|
542
|
+
if (cancellation.aborted) throw failure();
|
|
543
|
+
const result = await handler(params, cancellation);
|
|
544
|
+
if (generation !== this.generation || cancellation.aborted || this.active !== active)
|
|
545
|
+
throw failure();
|
|
546
|
+
return result;
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
if (this.tools.instructions) this.instance.setToolInstructions(this.tools.instructions);
|
|
550
|
+
const threadParams = {
|
|
551
|
+
model: this.config.model,
|
|
552
|
+
modelProvider: "openai",
|
|
553
|
+
cwd: this.instance.cwd,
|
|
554
|
+
approvalPolicy: tuning.approvalPolicy,
|
|
555
|
+
permissions: "managed",
|
|
556
|
+
config: {
|
|
557
|
+
...nativePolicy,
|
|
558
|
+
...privatePolicy.config,
|
|
559
|
+
mcp_servers: this.tools.mcpServers,
|
|
560
|
+
model_reasoning_effort: this.config.effort ?? tuning.reasoningEffort
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
const resuming = Boolean(this.config.resumeSessionId);
|
|
564
|
+
this.threadUsageTotal = resuming ? this.threadUsageTotal ?? {} : {};
|
|
565
|
+
this.resumeUsageTurnIds.clear();
|
|
566
|
+
const response = resuming ? await this.client.request("thread/resume", {
|
|
567
|
+
...threadParams,
|
|
568
|
+
threadId: this.config.resumeSessionId,
|
|
569
|
+
excludeTurns: false
|
|
570
|
+
}) : await this.client.request("thread/start", {
|
|
571
|
+
...threadParams,
|
|
572
|
+
allowProviderModelFallback: false
|
|
573
|
+
});
|
|
574
|
+
if (!record(response) || response.model !== this.config.model)
|
|
575
|
+
throw failure("CODEX_MODEL_UNAVAILABLE");
|
|
576
|
+
if (!record(response.thread) || typeof response.thread.id !== "string" || resuming && response.thread.id !== this.config.resumeSessionId || resuming && !Array.isArray(response.thread.turns) || response.reasoningEffort !== (this.config.effort ?? tuning.reasoningEffort) || response.modelProvider !== "openai" || response.approvalPolicy !== tuning.approvalPolicy)
|
|
577
|
+
throw failure("AI_PROFILE_INVALID");
|
|
578
|
+
if (generation !== this.generation || this.killed) throw failure();
|
|
579
|
+
this.resumeUsageTurnIds = new Set(
|
|
580
|
+
resuming && Array.isArray(response.thread.turns) ? response.thread.turns.flatMap(
|
|
581
|
+
(turn) => record(turn) && typeof turn.id === "string" ? [turn.id] : []
|
|
582
|
+
) : []
|
|
583
|
+
);
|
|
584
|
+
this.nativeApprovalPolicy = activateNativeApprovalPolicy(
|
|
585
|
+
privatePolicy,
|
|
586
|
+
response,
|
|
587
|
+
this.controller.signal
|
|
588
|
+
);
|
|
589
|
+
this.threadId = response.thread.id;
|
|
590
|
+
this.mcpStartup = this.pendingMcpStartup?.threadId === this.threadId ? this.pendingMcpStartup.status : "unknown";
|
|
591
|
+
this.pendingMcpStartup = void 0;
|
|
592
|
+
this.lastThreadId = this.threadId;
|
|
593
|
+
this.instance.bindThread(this.threadId);
|
|
594
|
+
}
|
|
595
|
+
async authenticate(timeout) {
|
|
596
|
+
const credential = this.credential;
|
|
597
|
+
if (credential.kind === "openai-api-key") {
|
|
598
|
+
const result = await this.client.request("account/login/start", {
|
|
599
|
+
type: "apiKey",
|
|
600
|
+
apiKey: credential.apiKey
|
|
601
|
+
});
|
|
602
|
+
if (result.type !== "apiKey") throw failure("CODEX_AUTH_UNAVAILABLE");
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
const completion = new Promise((resolve, reject) => {
|
|
606
|
+
this.loginCompleted = { resolve, reject };
|
|
607
|
+
});
|
|
608
|
+
const wait = bounded(completion, this.controller.signal, timeout);
|
|
609
|
+
void wait.catch(() => {
|
|
610
|
+
});
|
|
611
|
+
try {
|
|
612
|
+
const result = await this.client.request("account/login/start", {
|
|
613
|
+
type: "chatgptAuthTokens",
|
|
614
|
+
accessToken: credential.accessToken,
|
|
615
|
+
chatgptAccountId: credential.chatgptAccountId,
|
|
616
|
+
chatgptPlanType: credential.chatgptPlanType ?? null
|
|
617
|
+
});
|
|
618
|
+
if (result.type !== "chatgptAuthTokens") throw failure("CODEX_AUTH_UNAVAILABLE");
|
|
619
|
+
await wait;
|
|
620
|
+
} finally {
|
|
621
|
+
this.loginCompleted = void 0;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
async refresh(params, signal) {
|
|
625
|
+
const credential = this.credential;
|
|
626
|
+
if (!record(params) || params.reason !== "unauthorized" || !credential || credential.kind !== "codex-subscription" || params.previousAccountId != null && params.previousAccountId !== credential.chatgptAccountId || !this.options?.refreshCredential || (this.active ? this.active.refreshUsed : this.startupRefreshUsed))
|
|
627
|
+
throw failure("CODEX_AUTH_UNAVAILABLE");
|
|
628
|
+
if (this.active) this.active.refreshUsed = true;
|
|
629
|
+
else this.startupRefreshUsed = true;
|
|
630
|
+
const result = await this.options.refreshCredential(
|
|
631
|
+
{
|
|
632
|
+
kind: "codex-subscription",
|
|
633
|
+
configId: credential.configId,
|
|
634
|
+
credentialVersion: credential.credentialVersion,
|
|
635
|
+
reason: "retry-401"
|
|
636
|
+
},
|
|
637
|
+
signal
|
|
638
|
+
);
|
|
639
|
+
if (signal.aborted) throw failure("CODEX_AUTH_UNAVAILABLE");
|
|
640
|
+
let parsed;
|
|
641
|
+
try {
|
|
642
|
+
parsed = parseCodexRefreshResult(result, credential);
|
|
643
|
+
} catch {
|
|
644
|
+
throw failure("CODEX_AUTH_UNAVAILABLE");
|
|
645
|
+
}
|
|
646
|
+
if (!parsed.ok) throw failure("CODEX_AUTH_UNAVAILABLE");
|
|
647
|
+
this.credential = parsed.credential;
|
|
648
|
+
return {
|
|
649
|
+
accessToken: parsed.credential.accessToken,
|
|
650
|
+
chatgptAccountId: parsed.credential.chatgptAccountId,
|
|
651
|
+
chatgptPlanType: parsed.credential.chatgptPlanType ?? null
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
prompt(message) {
|
|
655
|
+
return this.promptInput({ text: message });
|
|
656
|
+
}
|
|
657
|
+
promptInput(input) {
|
|
658
|
+
const message = structuredClone(input);
|
|
659
|
+
const turnBudget = this.config.driverOptions?.codex?.managedRuntime?.nativeTurnBudget;
|
|
660
|
+
if (this.killed || this.broken || this.resetting || this.queued >= 32 || turnBudget !== void 0 && this.admittedTurns >= turnBudget)
|
|
661
|
+
return Promise.reject(failure());
|
|
662
|
+
if (turnBudget !== void 0) this.admittedTurns++;
|
|
663
|
+
this.queued++;
|
|
664
|
+
const task = this.queue.then(() => this.runTurn(message));
|
|
665
|
+
this.queue = task.catch(() => {
|
|
666
|
+
}).finally(() => {
|
|
667
|
+
this.queued--;
|
|
668
|
+
});
|
|
669
|
+
return task;
|
|
670
|
+
}
|
|
671
|
+
async runTurn(message) {
|
|
672
|
+
if (this.resetting) throw failure();
|
|
673
|
+
await this.start();
|
|
674
|
+
if (this.killed || this.broken || this.resetting) throw failure();
|
|
675
|
+
if (this.pendingPatch) {
|
|
676
|
+
this.config = { ...this.config, ...this.pendingPatch };
|
|
677
|
+
this.pendingPatch = void 0;
|
|
678
|
+
}
|
|
679
|
+
if (!this.config.model) throw failure("AI_PROFILE_INVALID");
|
|
680
|
+
await this.tools?.validate?.();
|
|
681
|
+
if (this.tools?.needsReload?.()) {
|
|
682
|
+
this.config = { ...this.config, resumeSessionId: this.threadId };
|
|
683
|
+
await this.closeGeneration();
|
|
684
|
+
await this.start();
|
|
685
|
+
}
|
|
686
|
+
const input = codexPromptInput(
|
|
687
|
+
message,
|
|
688
|
+
this.instance.cwd,
|
|
689
|
+
this.options?.skillsDirectory ? {
|
|
690
|
+
source: this.options.skillsDirectory,
|
|
691
|
+
target: join(this.instance.home, "skills")
|
|
692
|
+
} : void 0
|
|
693
|
+
);
|
|
694
|
+
try {
|
|
695
|
+
parseCodexCredential(this.credential);
|
|
696
|
+
} catch {
|
|
697
|
+
throw failure("CODEX_AUTH_UNAVAILABLE");
|
|
698
|
+
}
|
|
699
|
+
let finish;
|
|
700
|
+
let readyNow;
|
|
701
|
+
const ready = new Promise((resolve) => {
|
|
702
|
+
readyNow = resolve;
|
|
703
|
+
});
|
|
704
|
+
const done = new Promise((resolve) => {
|
|
705
|
+
finish = resolve;
|
|
706
|
+
});
|
|
707
|
+
const active = {
|
|
708
|
+
controller: new AbortController(),
|
|
709
|
+
done,
|
|
710
|
+
finish,
|
|
711
|
+
ready,
|
|
712
|
+
readyNow,
|
|
713
|
+
timer: setTimeout(
|
|
714
|
+
() => {
|
|
715
|
+
this.finishTurn("CODEX_RUNTIME_UNSUPPORTED");
|
|
716
|
+
this.broken = true;
|
|
717
|
+
void this.closeGeneration();
|
|
718
|
+
},
|
|
719
|
+
limit(this.options?.turnTimeoutMs, 3e5)
|
|
720
|
+
),
|
|
721
|
+
events: [],
|
|
722
|
+
messages: /* @__PURE__ */ new Map(),
|
|
723
|
+
fileChanges: /* @__PURE__ */ new Map(),
|
|
724
|
+
toolResults: [],
|
|
725
|
+
toolItems: /* @__PURE__ */ new Set(),
|
|
726
|
+
completedToolItems: /* @__PURE__ */ new Set(),
|
|
727
|
+
refreshUsed: false,
|
|
728
|
+
usageBaseline: this.threadUsageTotal,
|
|
729
|
+
actualModel: this.config.model
|
|
730
|
+
};
|
|
731
|
+
this.active = active;
|
|
732
|
+
this.mcpDiagnostics.nativeMcpToolItemCount = 0;
|
|
733
|
+
this.observeMcp(true);
|
|
734
|
+
this.tools?.beginTurn?.(active.controller.signal);
|
|
735
|
+
try {
|
|
736
|
+
const response = await this.client.request("turn/start", {
|
|
737
|
+
threadId: this.threadId,
|
|
738
|
+
permissions: "managed",
|
|
739
|
+
input,
|
|
740
|
+
model: this.config.model,
|
|
741
|
+
effort: this.config.effort ?? this.config.driverOptions.codex.reasoningEffort
|
|
742
|
+
});
|
|
743
|
+
if (this.active !== active) return;
|
|
744
|
+
if (!record(response.turn) || typeof response.turn.id !== "string") throw failure();
|
|
745
|
+
active.id = response.turn.id;
|
|
746
|
+
active.readyNow();
|
|
747
|
+
for (const event of active.events.splice(0)) this.notification(event.method, event.params);
|
|
748
|
+
if (this.active === active && response.turn.status !== "inProgress")
|
|
749
|
+
this.complete(response.turn);
|
|
750
|
+
await done;
|
|
751
|
+
} catch {
|
|
752
|
+
this.finishTurn("CODEX_RUNTIME_UNSUPPORTED");
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
async abort() {
|
|
756
|
+
const active = this.active;
|
|
757
|
+
if (!active) return;
|
|
758
|
+
active.controller.abort();
|
|
759
|
+
try {
|
|
760
|
+
if (!active.id)
|
|
761
|
+
await bounded(
|
|
762
|
+
active.ready,
|
|
763
|
+
this.controller.signal,
|
|
764
|
+
limit(this.options?.requestTimeoutMs, 15e3)
|
|
765
|
+
);
|
|
766
|
+
if (this.active !== active) return;
|
|
767
|
+
await this.client.request("turn/interrupt", {
|
|
768
|
+
threadId: this.threadId,
|
|
769
|
+
turnId: active.id
|
|
770
|
+
});
|
|
771
|
+
await bounded(
|
|
772
|
+
active.done,
|
|
773
|
+
this.controller.signal,
|
|
774
|
+
limit(this.options?.requestTimeoutMs, 15e3)
|
|
775
|
+
);
|
|
776
|
+
} catch {
|
|
777
|
+
this.finishTurn("CODEX_RUNTIME_UNSUPPORTED");
|
|
778
|
+
this.broken = true;
|
|
779
|
+
await this.closeGeneration();
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
kill() {
|
|
783
|
+
if (this.killed) return;
|
|
784
|
+
this.killed = true;
|
|
785
|
+
this.finishTurn("CODEX_RUNTIME_UNSUPPORTED");
|
|
786
|
+
void this.closeGeneration();
|
|
787
|
+
}
|
|
788
|
+
async dispose() {
|
|
789
|
+
this.kill();
|
|
790
|
+
await this.starting?.catch(() => {
|
|
791
|
+
});
|
|
792
|
+
await this.closing;
|
|
793
|
+
}
|
|
794
|
+
async resetSession() {
|
|
795
|
+
if (this.killed || this.broken) throw failure();
|
|
796
|
+
if (this.resetting) return this.resetting;
|
|
797
|
+
this.resetting = Promise.resolve().then(async () => {
|
|
798
|
+
if (this.active) await this.abort();
|
|
799
|
+
await this.queue;
|
|
800
|
+
await this.starting;
|
|
801
|
+
await this.closeGeneration();
|
|
802
|
+
if (this.killed || this.broken) throw failure();
|
|
803
|
+
this.config = { ...this.config, resumeSessionId: void 0 };
|
|
804
|
+
this.lastThreadId = void 0;
|
|
805
|
+
this.threadUsageTotal = void 0;
|
|
806
|
+
}).finally(() => {
|
|
807
|
+
this.resetting = void 0;
|
|
808
|
+
});
|
|
809
|
+
return this.resetting;
|
|
810
|
+
}
|
|
811
|
+
reconfigure(patch) {
|
|
812
|
+
this.pendingPatch = { ...this.pendingPatch, ...patch };
|
|
813
|
+
}
|
|
814
|
+
closeGeneration() {
|
|
815
|
+
++this.generation;
|
|
816
|
+
this.mcpStartup = "unknown";
|
|
817
|
+
this.pendingMcpStartup = void 0;
|
|
818
|
+
this.controller.abort();
|
|
819
|
+
this.nativeApprovalPolicy = void 0;
|
|
820
|
+
this.loginCompleted?.reject(failure());
|
|
821
|
+
this.loginCompleted = void 0;
|
|
822
|
+
const client = this.client;
|
|
823
|
+
this.client = void 0;
|
|
824
|
+
const connecting = this.connecting;
|
|
825
|
+
this.connecting = void 0;
|
|
826
|
+
const tools = this.tools;
|
|
827
|
+
this.tools = void 0;
|
|
828
|
+
const instance = this.instance;
|
|
829
|
+
this.instance = void 0;
|
|
830
|
+
this.threadId = void 0;
|
|
831
|
+
this.closing = this.closing.then(async () => {
|
|
832
|
+
try {
|
|
833
|
+
const pendingClient = await connecting?.catch(() => void 0);
|
|
834
|
+
await client?.dispose();
|
|
835
|
+
if (pendingClient && pendingClient !== client) await pendingClient.dispose();
|
|
836
|
+
if (client && !client.terminationConfirmed || pendingClient && !pendingClient.terminationConfirmed)
|
|
837
|
+
this.cleanupUncertain = true;
|
|
838
|
+
if (tools)
|
|
839
|
+
await bounded(
|
|
840
|
+
Promise.resolve().then(() => tools.dispose()),
|
|
841
|
+
new AbortController().signal,
|
|
842
|
+
1e3
|
|
843
|
+
).catch(() => {
|
|
844
|
+
this.cleanupUncertain = true;
|
|
845
|
+
});
|
|
846
|
+
} finally {
|
|
847
|
+
instance?.release();
|
|
848
|
+
}
|
|
849
|
+
}).catch(() => {
|
|
850
|
+
this.cleanupUncertain = true;
|
|
851
|
+
});
|
|
852
|
+
return this.closing;
|
|
853
|
+
}
|
|
854
|
+
notification(method, params) {
|
|
855
|
+
if (!record(params)) return;
|
|
856
|
+
if (method === "mcpServer/startupStatus/updated") {
|
|
857
|
+
const serverName = this.tools?.diagnostics?.serverName;
|
|
858
|
+
if (!serverName || params.name !== serverName || typeof params.threadId !== "string" || params.threadId.length > 512 || typeof params.status !== "string" || !["starting", "ready", "failed", "cancelled"].includes(params.status))
|
|
859
|
+
return;
|
|
860
|
+
const status = params.status;
|
|
861
|
+
if (this.threadId) {
|
|
862
|
+
if (params.threadId === this.threadId) this.mcpStartup = status;
|
|
863
|
+
} else this.pendingMcpStartup = { threadId: params.threadId, status };
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
if (method === "account/login/completed") {
|
|
867
|
+
if (params.success === true && params.loginId == null) this.loginCompleted?.resolve();
|
|
868
|
+
else this.loginCompleted?.reject(failure("CODEX_AUTH_UNAVAILABLE"));
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
const active = this.active;
|
|
872
|
+
if ((!active || !active.usageCursor && this.resumeUsageTurnIds.has(String(params.turnId))) && method === "thread/tokenUsage/updated" && params.threadId === (this.threadId ?? this.config.resumeSessionId) && record(params.tokenUsage) && record(params.tokenUsage.total)) {
|
|
873
|
+
this.threadUsageTotal = nativeUsage(params.tokenUsage.total);
|
|
874
|
+
if (active) active.usageBaseline = this.threadUsageTotal;
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
if (!active || params.threadId !== this.threadId) return;
|
|
878
|
+
if (!active.id) {
|
|
879
|
+
if (active.events.length >= 256) {
|
|
880
|
+
this.broken = true;
|
|
881
|
+
this.finishTurn("CODEX_RUNTIME_UNSUPPORTED");
|
|
882
|
+
void this.closeGeneration();
|
|
883
|
+
} else active.events.push({ method, params });
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
const turnId = record(params.turn) ? params.turn.id : params.turnId;
|
|
887
|
+
if (turnId !== active.id) return;
|
|
888
|
+
if (method === "thread/tokenUsage/updated" && record(params.tokenUsage)) {
|
|
889
|
+
const last = params.tokenUsage.last;
|
|
890
|
+
const total = params.tokenUsage.total;
|
|
891
|
+
const cumulativeUsage = record(total) ? nativeUsage(total) : void 0;
|
|
892
|
+
if (cumulativeUsage) {
|
|
893
|
+
active.usageBaseline ??= {};
|
|
894
|
+
this.resumeUsageTurnIds.clear();
|
|
895
|
+
const cursor = active.usageCursor ?? active.usageBaseline;
|
|
896
|
+
const reset = cursor?.totalTokens !== void 0 && cumulativeUsage.totalTokens !== void 0 && cumulativeUsage.totalTokens < cursor.totalTokens;
|
|
897
|
+
const increment = reset ? cumulativeUsage : subtractUsage(cumulativeUsage, cursor);
|
|
898
|
+
if (Object.values(increment).some((value) => typeof value === "number" && value > 0))
|
|
899
|
+
active.usage = addUsage(active.usage, increment);
|
|
900
|
+
active.usageCursor = cumulativeUsage;
|
|
901
|
+
this.threadUsageTotal = cumulativeUsage;
|
|
902
|
+
}
|
|
903
|
+
if (record(last)) {
|
|
904
|
+
if (typeof last.totalTokens === "number") active.contextTokens = last.totalTokens;
|
|
905
|
+
}
|
|
906
|
+
if (typeof params.tokenUsage.modelContextWindow === "number")
|
|
907
|
+
active.contextWindow = params.tokenUsage.modelContextWindow;
|
|
908
|
+
} else if (method === "model/rerouted") {
|
|
909
|
+
active.actualModel = typeof params.toModel === "string" ? params.toModel : active.actualModel;
|
|
910
|
+
active.failureCode = "CODEX_MODEL_UNAVAILABLE";
|
|
911
|
+
} else if (method === "turn/completed" && record(params.turn)) this.complete(params.turn);
|
|
912
|
+
else if (method === "error" && params.willRetry !== true)
|
|
913
|
+
active.failureCode = this.nativeErrorCode(params.error);
|
|
914
|
+
else if (method === "item/agentMessage/delta" && typeof params.itemId === "string" && typeof params.delta === "string") {
|
|
915
|
+
const previous = active.messages.get(params.itemId);
|
|
916
|
+
if (previous === void 0)
|
|
917
|
+
this.event({
|
|
918
|
+
type: "message_start",
|
|
919
|
+
message: { role: "assistant", content: [{ type: "text", text: "" }] }
|
|
920
|
+
});
|
|
921
|
+
const text = (previous ?? "") + params.delta;
|
|
922
|
+
active.messages.set(params.itemId, text);
|
|
923
|
+
this.event({
|
|
924
|
+
type: "message_update",
|
|
925
|
+
message: { role: "assistant", content: [{ type: "text", text }] },
|
|
926
|
+
_textDelta: params.delta
|
|
927
|
+
});
|
|
928
|
+
} else if ((method === "item/started" || method === "item/completed") && record(params.item) && typeof params.item.id === "string") {
|
|
929
|
+
this.item(
|
|
930
|
+
params.item,
|
|
931
|
+
method === "item/completed"
|
|
932
|
+
);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
item(item, completed) {
|
|
936
|
+
const active = this.active;
|
|
937
|
+
if (item.type === "fileChange") {
|
|
938
|
+
active.fileChanges.delete(item.id);
|
|
939
|
+
if (!completed && Array.isArray(item.changes) && item.changes.length <= 16 && active.fileChanges.size < 16) {
|
|
940
|
+
const snapshot = JSON.stringify(item.changes);
|
|
941
|
+
if (snapshot.length <= 65536) active.fileChanges.set(item.id, snapshot);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
if (item.type === "agentMessage") {
|
|
945
|
+
if (completed && typeof item.text === "string") {
|
|
946
|
+
const previous = active.messages.get(item.id) ?? "";
|
|
947
|
+
if (!active.messages.has(item.id))
|
|
948
|
+
this.event({
|
|
949
|
+
type: "message_start",
|
|
950
|
+
message: {
|
|
951
|
+
role: "assistant",
|
|
952
|
+
content: [{ type: "text", text: "" }]
|
|
953
|
+
}
|
|
954
|
+
});
|
|
955
|
+
if (item.text.startsWith(previous) && item.text.length > previous.length)
|
|
956
|
+
this.event({
|
|
957
|
+
type: "message_update",
|
|
958
|
+
message: {
|
|
959
|
+
role: "assistant",
|
|
960
|
+
content: [{ type: "text", text: item.text }]
|
|
961
|
+
},
|
|
962
|
+
_textDelta: item.text.slice(previous.length)
|
|
963
|
+
});
|
|
964
|
+
active.messages.set(item.id, item.text);
|
|
965
|
+
}
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
const name = item.type === "commandExecution" ? "Bash" : item.type === "fileChange" ? "ApplyPatch" : item.type === "mcpToolCall" ? `mcp__${item.server}__${item.tool}` : void 0;
|
|
969
|
+
if (!name) return;
|
|
970
|
+
if (!active.toolItems.has(item.id)) {
|
|
971
|
+
if (item.type === "mcpToolCall" && item.server === this.tools?.diagnostics?.serverName)
|
|
972
|
+
this.mcpDiagnostics.nativeMcpToolItemCount = Math.min(
|
|
973
|
+
512,
|
|
974
|
+
this.mcpDiagnostics.nativeMcpToolItemCount + 1
|
|
975
|
+
);
|
|
976
|
+
active.toolItems.add(item.id);
|
|
977
|
+
this.event({
|
|
978
|
+
type: "tool_call",
|
|
979
|
+
name,
|
|
980
|
+
tool: { name },
|
|
981
|
+
toolCallId: item.id,
|
|
982
|
+
input: item.type === "mcpToolCall" ? item.arguments : item.type === "commandExecution" ? { command: item.command } : item.type === "fileChange" ? { changes: item.changes } : {}
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
if (completed && !active.completedToolItems.has(item.id)) {
|
|
986
|
+
active.completedToolItems.add(item.id);
|
|
987
|
+
active.toolResults.push({
|
|
988
|
+
role: "tool",
|
|
989
|
+
toolName: name,
|
|
990
|
+
content: "",
|
|
991
|
+
data: item.type === "mcpToolCall" ? {
|
|
992
|
+
content: item.result?.content,
|
|
993
|
+
structuredContent: item.result?.structuredContent,
|
|
994
|
+
status: item.status
|
|
995
|
+
} : item.type === "commandExecution" ? {
|
|
996
|
+
output: item.aggregatedOutput,
|
|
997
|
+
exitCode: item.exitCode,
|
|
998
|
+
status: item.status
|
|
999
|
+
} : item.type === "fileChange" ? { changes: item.changes, status: item.status } : {},
|
|
1000
|
+
isError: "status" in item && item.status === "failed"
|
|
1001
|
+
});
|
|
1002
|
+
this.event({ type: "tool_execution_end", toolName: name });
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
complete(turn) {
|
|
1006
|
+
if (turn.status === "completed") this.finishTurn(this.active?.failureCode);
|
|
1007
|
+
else if (turn.status === "interrupted") this.finishTurn("CODEX_TURN_INTERRUPTED");
|
|
1008
|
+
else if (turn.status === "failed")
|
|
1009
|
+
this.finishTurn(this.active?.failureCode ?? this.nativeErrorCode(turn.error));
|
|
1010
|
+
}
|
|
1011
|
+
nativeErrorCode(error) {
|
|
1012
|
+
const info = record(error) ? error.codexErrorInfo : void 0;
|
|
1013
|
+
if (info === "unauthorized") return "CODEX_RECONNECT_REQUIRED";
|
|
1014
|
+
if (info === "usageLimitExceeded" || info === "rateLimitExceeded" || info === "serverOverloaded")
|
|
1015
|
+
return "CODEX_RATE_LIMITED";
|
|
1016
|
+
if (record(info) && [
|
|
1017
|
+
"httpConnectionFailed",
|
|
1018
|
+
"responseStreamConnectionFailed",
|
|
1019
|
+
"responseStreamDisconnected",
|
|
1020
|
+
"responseTooManyFailedAttempts"
|
|
1021
|
+
].some((key) => key in info))
|
|
1022
|
+
return "CODEX_NETWORK_UNAVAILABLE";
|
|
1023
|
+
return "CODEX_RUNTIME_UNSUPPORTED";
|
|
1024
|
+
}
|
|
1025
|
+
observeMcp(beforeTurn) {
|
|
1026
|
+
const count = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? Math.min(512, value) : 0;
|
|
1027
|
+
let listed = 0;
|
|
1028
|
+
let called = 0;
|
|
1029
|
+
try {
|
|
1030
|
+
const counts = this.tools?.diagnostics?.requestCounts();
|
|
1031
|
+
listed = count(counts?.toolsListTotal);
|
|
1032
|
+
called = count(counts?.toolsCallTotal);
|
|
1033
|
+
} catch {
|
|
1034
|
+
}
|
|
1035
|
+
if (beforeTurn) {
|
|
1036
|
+
this.mcpDiagnostics.startupBeforeTurn = this.mcpStartup;
|
|
1037
|
+
this.mcpDiagnostics.toolsListBeforeTurn = listed;
|
|
1038
|
+
}
|
|
1039
|
+
this.mcpDiagnostics.startupAtEnd = this.mcpStartup;
|
|
1040
|
+
this.mcpDiagnostics.toolsListTotal = Math.max(this.mcpDiagnostics.toolsListBeforeTurn, listed);
|
|
1041
|
+
this.mcpDiagnostics.toolsCallTotal = beforeTurn ? called : Math.max(this.mcpDiagnostics.toolsCallTotal, called);
|
|
1042
|
+
try {
|
|
1043
|
+
void Promise.resolve(
|
|
1044
|
+
this.options?.onNativeMcpDiagnostics?.(Object.freeze({ ...this.mcpDiagnostics }))
|
|
1045
|
+
).catch(() => {
|
|
1046
|
+
});
|
|
1047
|
+
} catch {
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
finishTurn(code) {
|
|
1051
|
+
const active = this.active;
|
|
1052
|
+
if (!active) return;
|
|
1053
|
+
this.active = void 0;
|
|
1054
|
+
this.observeMcp(false);
|
|
1055
|
+
clearTimeout(active.timer);
|
|
1056
|
+
active.controller.abort();
|
|
1057
|
+
active.fileChanges.clear();
|
|
1058
|
+
active.readyNow();
|
|
1059
|
+
for (const text of active.messages.values())
|
|
1060
|
+
this.event({
|
|
1061
|
+
type: "message_end",
|
|
1062
|
+
message: { role: "assistant", content: [{ type: "text", text }] }
|
|
1063
|
+
});
|
|
1064
|
+
this.event({ type: "turn_end", toolResults: active.toolResults });
|
|
1065
|
+
const modelUsage = active.usage ? {
|
|
1066
|
+
inputTokens: active.usage.inputTokens,
|
|
1067
|
+
outputTokens: active.usage.outputTokens,
|
|
1068
|
+
cacheReadTokens: active.usage.cacheReadTokens,
|
|
1069
|
+
...typeof active.contextWindow === "number" ? { contextWindow: active.contextWindow } : {}
|
|
1070
|
+
} : void 0;
|
|
1071
|
+
const usage = {
|
|
1072
|
+
...active.usage ? { tokens: active.usage } : {},
|
|
1073
|
+
...modelUsage ? { modelUsage: { [active.actualModel]: modelUsage } } : {},
|
|
1074
|
+
...typeof active.contextTokens === "number" ? { contextTokens: active.contextTokens } : {},
|
|
1075
|
+
...typeof active.contextWindow === "number" ? { contextWindow: active.contextWindow } : {}
|
|
1076
|
+
};
|
|
1077
|
+
if (code)
|
|
1078
|
+
this.event({
|
|
1079
|
+
type: "error",
|
|
1080
|
+
error: code,
|
|
1081
|
+
fatal: code !== "CODEX_TURN_INTERRUPTED",
|
|
1082
|
+
detail: {
|
|
1083
|
+
message: code,
|
|
1084
|
+
category: code === "CODEX_TURN_INTERRUPTED" ? "process" : code === "CODEX_RATE_LIMITED" ? "rate_limit" : code === "CODEX_NETWORK_UNAVAILABLE" ? "network" : code === "CODEX_AUTH_UNAVAILABLE" || code === "CODEX_RECONNECT_REQUIRED" ? "auth" : "config",
|
|
1085
|
+
retryable: false
|
|
1086
|
+
},
|
|
1087
|
+
...usage
|
|
1088
|
+
});
|
|
1089
|
+
else
|
|
1090
|
+
this.event({
|
|
1091
|
+
type: "result",
|
|
1092
|
+
subtype: "success",
|
|
1093
|
+
summary: [...active.messages.values()].join("\n"),
|
|
1094
|
+
...usage
|
|
1095
|
+
});
|
|
1096
|
+
this.event({ type: "agent_end" });
|
|
1097
|
+
active.finish();
|
|
1098
|
+
}
|
|
1099
|
+
event(event) {
|
|
1100
|
+
try {
|
|
1101
|
+
this.emit("agent-event", event);
|
|
1102
|
+
} catch {
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
};
|
|
12
1106
|
function buildCodexChildEnv(parent) {
|
|
13
1107
|
const out = {};
|
|
14
1108
|
const explicit = [
|
|
@@ -38,6 +1132,7 @@ var CodexDriver = class extends AgentDriver {
|
|
|
38
1132
|
};
|
|
39
1133
|
log = getBridgeLogger("codex");
|
|
40
1134
|
config;
|
|
1135
|
+
managed;
|
|
41
1136
|
thread = null;
|
|
42
1137
|
running = false;
|
|
43
1138
|
abortController = null;
|
|
@@ -50,11 +1145,33 @@ var CodexDriver = class extends AgentDriver {
|
|
|
50
1145
|
constructor(config) {
|
|
51
1146
|
super();
|
|
52
1147
|
this.config = config;
|
|
1148
|
+
const codex = config.driverOptions?.codex;
|
|
1149
|
+
if (codex?.managedRuntime || codex?.authSource !== void 0 && codex.authSource !== "local") {
|
|
1150
|
+
this.managed = new ManagedCodexDriver(config);
|
|
1151
|
+
this.managed.on("agent-event", (event) => this.emit("agent-event", event));
|
|
1152
|
+
}
|
|
53
1153
|
}
|
|
54
1154
|
get runtimeSessionId() {
|
|
55
|
-
return this._runtimeSessionId;
|
|
1155
|
+
return this.managed ? this.managed.runtimeSessionId : this._runtimeSessionId;
|
|
1156
|
+
}
|
|
1157
|
+
get acceptsReplyAsPrompt() {
|
|
1158
|
+
return !this.managed;
|
|
1159
|
+
}
|
|
1160
|
+
async dispose() {
|
|
1161
|
+
if (this.managed) return this.managed.dispose();
|
|
1162
|
+
this.kill();
|
|
1163
|
+
}
|
|
1164
|
+
hasPendingQuestion() {
|
|
1165
|
+
return this.managed?.hasPendingQuestion() ?? false;
|
|
1166
|
+
}
|
|
1167
|
+
answerQuestion(answer, question, requestId) {
|
|
1168
|
+
return this.managed?.answerQuestion(answer, question, requestId) ?? false;
|
|
1169
|
+
}
|
|
1170
|
+
promptInput(input) {
|
|
1171
|
+
return this.managed ? this.managed.promptInput(input) : super.promptInput(input);
|
|
56
1172
|
}
|
|
57
1173
|
async start() {
|
|
1174
|
+
if (this.managed) return this.managed.start();
|
|
58
1175
|
if (this.thread) return;
|
|
59
1176
|
let sdk;
|
|
60
1177
|
try {
|
|
@@ -90,6 +1207,7 @@ var CodexDriver = class extends AgentDriver {
|
|
|
90
1207
|
});
|
|
91
1208
|
}
|
|
92
1209
|
async prompt(message) {
|
|
1210
|
+
if (this.managed) return this.managed.prompt(message);
|
|
93
1211
|
await this.start();
|
|
94
1212
|
if (!this.thread) throw new Error("Codex thread not initialized");
|
|
95
1213
|
if (this.running) throw new Error("Codex turn already in progress");
|
|
@@ -135,32 +1253,47 @@ var CodexDriver = class extends AgentDriver {
|
|
|
135
1253
|
}
|
|
136
1254
|
}
|
|
137
1255
|
async abort() {
|
|
1256
|
+
if (this.managed) return this.managed.abort();
|
|
138
1257
|
this.abortController?.abort();
|
|
139
1258
|
}
|
|
140
1259
|
get isRunning() {
|
|
1260
|
+
if (this.managed) return this.managed.isRunning;
|
|
141
1261
|
return this.running;
|
|
142
1262
|
}
|
|
143
1263
|
kill() {
|
|
1264
|
+
if (this.managed) {
|
|
1265
|
+
this.managed.kill();
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
144
1268
|
this.abortController?.abort();
|
|
145
1269
|
this.running = false;
|
|
146
1270
|
this.abortController = null;
|
|
147
1271
|
this.log.info("codex driver killed");
|
|
148
1272
|
}
|
|
149
1273
|
async listModels() {
|
|
1274
|
+
if (this.managed) return this.managed.listModels();
|
|
150
1275
|
const apiKey = this.config.apiKeys?.openai || this.config.env?.OPENAI_API_KEY || process.env.OPENAI_API_KEY;
|
|
151
1276
|
if (!apiKey) return [];
|
|
152
1277
|
const result = await fetchProviderModels("openai", apiKey);
|
|
153
1278
|
return result.ok ? result.models : [];
|
|
154
1279
|
}
|
|
155
1280
|
async resetSession() {
|
|
1281
|
+
if (this.managed) return this.managed.resetSession();
|
|
156
1282
|
this.kill();
|
|
157
1283
|
this.thread = null;
|
|
158
1284
|
this._runtimeSessionId = void 0;
|
|
159
1285
|
this._lastTokens = null;
|
|
160
1286
|
}
|
|
161
1287
|
getTokenUsage() {
|
|
1288
|
+
if (this.managed) return this.managed.getTokenUsage();
|
|
162
1289
|
return this._lastTokens;
|
|
163
1290
|
}
|
|
1291
|
+
reconfigure(patch) {
|
|
1292
|
+
this.managed?.reconfigure(patch);
|
|
1293
|
+
}
|
|
1294
|
+
getModel() {
|
|
1295
|
+
return this.managed?.getModel();
|
|
1296
|
+
}
|
|
164
1297
|
/**
|
|
165
1298
|
* Write a combined instructions file for Codex CLI's `model_instructions_file` config.
|
|
166
1299
|
* Combines pre-rendered agent identity (from `skaile install`) with runtime context
|