@tiangong-ai/cli 0.0.60 → 0.0.62
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/AGENTS.md +17 -6
- package/README.md +577 -55
- package/dist/data/builtins.js +33 -5
- package/dist/data/builtins.js.map +1 -1
- package/dist/data/catalog.js +46 -0
- package/dist/data/catalog.js.map +1 -1
- package/dist/data/commands.js +15 -1
- package/dist/data/commands.js.map +1 -1
- package/dist/data/connectors/airnow-hourly-observations.js +60 -10
- package/dist/data/connectors/airnow-hourly-observations.js.map +1 -1
- package/dist/data/connectors/epa-eis-records.js +16 -2
- package/dist/data/connectors/epa-eis-records.js.map +1 -1
- package/dist/data/connectors/gdelt-doc-search.js +54 -18
- package/dist/data/connectors/gdelt-doc-search.js.map +1 -1
- package/dist/data/connectors/gdelt-file-feeds.js +20 -11
- package/dist/data/connectors/gdelt-file-feeds.js.map +1 -1
- package/dist/data/connectors/gdelt-web-ngrams.d.ts +2 -0
- package/dist/data/connectors/gdelt-web-ngrams.js +350 -0
- package/dist/data/connectors/gdelt-web-ngrams.js.map +1 -0
- package/dist/data/connectors/gdelt-web-ngrams.schemas.d.ts +188 -0
- package/dist/data/connectors/gdelt-web-ngrams.schemas.js +138 -0
- package/dist/data/connectors/gdelt-web-ngrams.schemas.js.map +1 -0
- package/dist/data/connectors/nasa-firms-fire.js +4 -0
- package/dist/data/connectors/nasa-firms-fire.js.map +1 -1
- package/dist/data/connectors/open-meteo-air-quality.js +27 -22
- package/dist/data/connectors/open-meteo-air-quality.js.map +1 -1
- package/dist/data/connectors/open-meteo-air-quality.schemas.d.ts +4 -1
- package/dist/data/connectors/open-meteo-air-quality.schemas.js +21 -1
- package/dist/data/connectors/open-meteo-air-quality.schemas.js.map +1 -1
- package/dist/data/connectors/open-meteo-flood.js +28 -23
- package/dist/data/connectors/open-meteo-flood.js.map +1 -1
- package/dist/data/connectors/open-meteo-flood.schemas.d.ts +4 -1
- package/dist/data/connectors/open-meteo-flood.schemas.js +21 -1
- package/dist/data/connectors/open-meteo-flood.schemas.js.map +1 -1
- package/dist/data/connectors/open-meteo-historical-weather.js +26 -21
- package/dist/data/connectors/open-meteo-historical-weather.js.map +1 -1
- package/dist/data/connectors/open-meteo-historical-weather.schemas.d.ts +4 -1
- package/dist/data/connectors/open-meteo-historical-weather.schemas.js +21 -1
- package/dist/data/connectors/open-meteo-historical-weather.schemas.js.map +1 -1
- package/dist/data/connectors/openaq-air-quality.js +50 -12
- package/dist/data/connectors/openaq-air-quality.js.map +1 -1
- package/dist/data/connectors/openaq-air-quality.schemas.d.ts +0 -4
- package/dist/data/connectors/openaq-air-quality.schemas.js +2 -2
- package/dist/data/connectors/openaq-air-quality.schemas.js.map +1 -1
- package/dist/data/connectors/youtube-public-content.js +76 -6
- package/dist/data/connectors/youtube-public-content.js.map +1 -1
- package/dist/data/connectors/youtube-public-content.schemas.d.ts +63 -3
- package/dist/data/connectors/youtube-public-content.schemas.js +56 -2
- package/dist/data/connectors/youtube-public-content.schemas.js.map +1 -1
- package/dist/data/contracts.d.ts +22 -1
- package/dist/data/runtime/bounded-http.d.ts +2 -0
- package/dist/data/runtime/bounded-http.js +221 -59
- package/dist/data/runtime/bounded-http.js.map +1 -1
- package/dist/data/runtime/execute.d.ts +2 -0
- package/dist/data/runtime/execute.js +82 -60
- package/dist/data/runtime/execute.js.map +1 -1
- package/dist/data/runtime/http-timing.d.ts +15 -0
- package/dist/data/runtime/http-timing.js +59 -0
- package/dist/data/runtime/http-timing.js.map +1 -0
- package/dist/data/runtime/http-transport.d.ts +1 -0
- package/dist/data/runtime/http-transport.js +27 -0
- package/dist/data/runtime/http-transport.js.map +1 -0
- package/dist/data/schemas/catalog.v1.json +46 -0
- package/dist/data/schemas/discovery.v1.json +12 -0
- package/dist/data/schemas/error.v1.json +1 -0
- package/dist/data/schemas/manifest.v1.json +19 -1
- package/dist/research/orchestration.js +341 -28
- package/dist/research/orchestration.js.map +1 -1
- package/dist/research/setup-command.js +37 -6
- package/dist/research/setup-command.js.map +1 -1
- package/dist/research/workspace/acquisition-routes.d.ts +1 -1
- package/dist/research/workspace/acquisition-routes.js +2 -11
- package/dist/research/workspace/acquisition-routes.js.map +1 -1
- package/dist/research/workspace/artifact-read-audit.d.ts +16 -0
- package/dist/research/workspace/artifact-read-audit.js +176 -0
- package/dist/research/workspace/artifact-read-audit.js.map +1 -0
- package/dist/research/workspace/artifact-trace.d.ts +12 -0
- package/dist/research/workspace/artifact-trace.js +109 -0
- package/dist/research/workspace/artifact-trace.js.map +1 -0
- package/dist/research/workspace/artifact-view-mcp.d.ts +75 -0
- package/dist/research/workspace/artifact-view-mcp.js +214 -0
- package/dist/research/workspace/artifact-view-mcp.js.map +1 -0
- package/dist/research/workspace/artifact-views.d.ts +100 -0
- package/dist/research/workspace/artifact-views.js +395 -0
- package/dist/research/workspace/artifact-views.js.map +1 -0
- package/dist/research/workspace/audit-bundle.d.ts +4 -0
- package/dist/research/workspace/audit-bundle.js +130 -11
- package/dist/research/workspace/audit-bundle.js.map +1 -1
- package/dist/research/workspace/broker.js +25 -4
- package/dist/research/workspace/broker.js.map +1 -1
- package/dist/research/workspace/constants.d.ts +1 -0
- package/dist/research/workspace/constants.js +1 -0
- package/dist/research/workspace/constants.js.map +1 -1
- package/dist/research/workspace/content-evidence.d.ts +1 -0
- package/dist/research/workspace/content-evidence.js +1 -1
- package/dist/research/workspace/content-evidence.js.map +1 -1
- package/dist/research/workspace/credentials.js +2 -1
- package/dist/research/workspace/credentials.js.map +1 -1
- package/dist/research/workspace/data-evidence-adapter.d.ts +71 -0
- package/dist/research/workspace/data-evidence-adapter.js +269 -59
- package/dist/research/workspace/data-evidence-adapter.js.map +1 -1
- package/dist/research/workspace/data-evidence-view.d.ts +58 -0
- package/dist/research/workspace/data-evidence-view.js +411 -0
- package/dist/research/workspace/data-evidence-view.js.map +1 -0
- package/dist/research/workspace/evidence-ledger.js +32 -29
- package/dist/research/workspace/evidence-ledger.js.map +1 -1
- package/dist/research/workspace/evidence.d.ts +28 -0
- package/dist/research/workspace/evidence.js +48 -0
- package/dist/research/workspace/evidence.js.map +1 -1
- package/dist/research/workspace/executor.d.ts +32 -2
- package/dist/research/workspace/executor.js +286 -42
- package/dist/research/workspace/executor.js.map +1 -1
- package/dist/research/workspace/investigation-attempt.d.ts +159 -0
- package/dist/research/workspace/investigation-attempt.js +619 -0
- package/dist/research/workspace/investigation-attempt.js.map +1 -0
- package/dist/research/workspace/investigation-audit.d.ts +20 -0
- package/dist/research/workspace/investigation-audit.js +306 -0
- package/dist/research/workspace/investigation-audit.js.map +1 -0
- package/dist/research/workspace/investigation-candidate.d.ts +84 -0
- package/dist/research/workspace/investigation-candidate.js +156 -0
- package/dist/research/workspace/investigation-candidate.js.map +1 -0
- package/dist/research/workspace/investigation-certification.d.ts +39 -0
- package/dist/research/workspace/investigation-certification.js +152 -0
- package/dist/research/workspace/investigation-certification.js.map +1 -0
- package/dist/research/workspace/investigation-close.d.ts +47 -0
- package/dist/research/workspace/investigation-close.js +153 -0
- package/dist/research/workspace/investigation-close.js.map +1 -0
- package/dist/research/workspace/investigation-lineage.d.ts +8 -0
- package/dist/research/workspace/investigation-lineage.js +10 -0
- package/dist/research/workspace/investigation-lineage.js.map +1 -0
- package/dist/research/workspace/investigation-observer.d.ts +22 -0
- package/dist/research/workspace/investigation-observer.js +122 -0
- package/dist/research/workspace/investigation-observer.js.map +1 -0
- package/dist/research/workspace/investigation-promotion.d.ts +129 -0
- package/dist/research/workspace/investigation-promotion.js +316 -0
- package/dist/research/workspace/investigation-promotion.js.map +1 -0
- package/dist/research/workspace/investigation-requirements.d.ts +5 -0
- package/dist/research/workspace/investigation-requirements.js +32 -0
- package/dist/research/workspace/investigation-requirements.js.map +1 -0
- package/dist/research/workspace/investigation-resources.d.ts +12 -0
- package/dist/research/workspace/investigation-resources.js +78 -0
- package/dist/research/workspace/investigation-resources.js.map +1 -0
- package/dist/research/workspace/investigation-source-export.d.ts +6 -0
- package/dist/research/workspace/investigation-source-export.js +68 -0
- package/dist/research/workspace/investigation-source-export.js.map +1 -0
- package/dist/research/workspace/investigation-store.d.ts +9 -0
- package/dist/research/workspace/investigation-store.js +55 -0
- package/dist/research/workspace/investigation-store.js.map +1 -0
- package/dist/research/workspace/investigation.d.ts +424 -0
- package/dist/research/workspace/investigation.js +716 -0
- package/dist/research/workspace/investigation.js.map +1 -0
- package/dist/research/workspace/native-process-supervisor.d.ts +1 -0
- package/dist/research/workspace/native-process-supervisor.js +97 -0
- package/dist/research/workspace/native-process-supervisor.js.map +1 -0
- package/dist/research/workspace/native-run.d.ts +150 -0
- package/dist/research/workspace/native-run.js +879 -0
- package/dist/research/workspace/native-run.js.map +1 -0
- package/dist/research/workspace/preflight.d.ts +11 -2
- package/dist/research/workspace/preflight.js +28 -20
- package/dist/research/workspace/preflight.js.map +1 -1
- package/dist/research/workspace/project-authority.js +2 -0
- package/dist/research/workspace/project-authority.js.map +1 -1
- package/dist/research/workspace/project-budget.d.ts +41 -0
- package/dist/research/workspace/project-budget.js +265 -0
- package/dist/research/workspace/project-budget.js.map +1 -0
- package/dist/research/workspace/project-mutations.d.ts +1 -1
- package/dist/research/workspace/project-mutations.js +29 -3
- package/dist/research/workspace/project-mutations.js.map +1 -1
- package/dist/research/workspace/projects.d.ts +66 -0
- package/dist/research/workspace/projects.js +182 -3
- package/dist/research/workspace/projects.js.map +1 -1
- package/dist/research/workspace/provider-budget.d.ts +3 -0
- package/dist/research/workspace/provider-budget.js +45 -0
- package/dist/research/workspace/provider-budget.js.map +1 -0
- package/dist/research/workspace/provider-state.d.ts +1 -0
- package/dist/research/workspace/provider-state.js +20 -0
- package/dist/research/workspace/provider-state.js.map +1 -0
- package/dist/research/workspace/publication-lineage.d.ts +34 -0
- package/dist/research/workspace/publication-lineage.js +165 -0
- package/dist/research/workspace/publication-lineage.js.map +1 -0
- package/dist/research/workspace/publication-workflow.d.ts +24 -0
- package/dist/research/workspace/publication-workflow.js +118 -14
- package/dist/research/workspace/publication-workflow.js.map +1 -1
- package/dist/research/workspace/request-provenance.d.ts +67 -0
- package/dist/research/workspace/request-provenance.js +115 -0
- package/dist/research/workspace/request-provenance.js.map +1 -0
- package/dist/research/workspace/review-executor.d.ts +6 -1
- package/dist/research/workspace/review-executor.js +39 -14
- package/dist/research/workspace/review-executor.js.map +1 -1
- package/dist/research/workspace/runtime.d.ts +64 -1
- package/dist/research/workspace/runtime.js +359 -112
- package/dist/research/workspace/runtime.js.map +1 -1
- package/dist/research/workspace/sanitization.js +1 -1
- package/dist/research/workspace/sanitization.js.map +1 -1
- package/dist/research/workspace/schema-compatibility.d.ts +5 -0
- package/dist/research/workspace/schema-compatibility.js +55 -0
- package/dist/research/workspace/schema-compatibility.js.map +1 -0
- package/dist/research/workspace/scientific-amendment.d.ts +103 -0
- package/dist/research/workspace/scientific-amendment.js +537 -0
- package/dist/research/workspace/scientific-amendment.js.map +1 -0
- package/dist/research/workspace/scientific-design.js +8 -17
- package/dist/research/workspace/scientific-design.js.map +1 -1
- package/dist/research/workspace/scientific-fulfillment-audit.d.ts +21 -0
- package/dist/research/workspace/scientific-fulfillment-audit.js +217 -0
- package/dist/research/workspace/scientific-fulfillment-audit.js.map +1 -0
- package/dist/research/workspace/scientific-fulfillment.d.ts +130 -0
- package/dist/research/workspace/scientific-fulfillment.js +553 -0
- package/dist/research/workspace/scientific-fulfillment.js.map +1 -0
- package/dist/research/workspace/scientific-review-execution.js +236 -75
- package/dist/research/workspace/scientific-review-execution.js.map +1 -1
- package/dist/research/workspace/scientific-review.d.ts +6 -1
- package/dist/research/workspace/scientific-review.js +228 -52
- package/dist/research/workspace/scientific-review.js.map +1 -1
- package/dist/research/workspace/setup-catalog.js +3 -3
- package/dist/research/workspace/setup-release.d.ts +22 -0
- package/dist/research/workspace/setup-release.js +426 -0
- package/dist/research/workspace/setup-release.js.map +1 -0
- package/dist/research/workspace/setup-runtime-integrity.d.ts +2 -0
- package/dist/research/workspace/setup-runtime-integrity.js +31 -0
- package/dist/research/workspace/setup-runtime-integrity.js.map +1 -0
- package/dist/research/workspace/setup-upgrade.d.ts +17 -0
- package/dist/research/workspace/setup-upgrade.js +784 -0
- package/dist/research/workspace/setup-upgrade.js.map +1 -0
- package/dist/research/workspace/setup.d.ts +31 -39
- package/dist/research/workspace/setup.js +220 -17
- package/dist/research/workspace/setup.js.map +1 -1
- package/dist/research/workspace/task-acceptance.d.ts +17 -2
- package/dist/research/workspace/task-acceptance.js +106 -10
- package/dist/research/workspace/task-acceptance.js.map +1 -1
- package/dist/research/workspace/task-audit.d.ts +5 -1
- package/dist/research/workspace/task-audit.js +70 -5
- package/dist/research/workspace/task-audit.js.map +1 -1
- package/dist/research/workspace/task-contract.d.ts +4 -1
- package/dist/research/workspace/task-contract.js +26 -5
- package/dist/research/workspace/task-contract.js.map +1 -1
- package/dist/research/workspace/types.d.ts +50 -3
- package/dist/research/workspace/workspace.js +34 -4
- package/dist/research/workspace/workspace.js.map +1 -1
- package/package.json +2 -1
|
@@ -6,8 +6,12 @@ import { arch, homedir, platform } from "node:os";
|
|
|
6
6
|
import { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { CliError } from "../../errors.js";
|
|
9
|
+
import { isArtifactViewBinding, openArtifactViews, reviewNetworkPolicy, } from "./artifact-views.js";
|
|
10
|
+
import { ARTIFACT_VIEW_TOOL_NAMES, startArtifactViewServer } from "./artifact-view-mcp.js";
|
|
11
|
+
import { codexArtifactTrace, isPacketArtifactTool } from "./artifact-trace.js";
|
|
9
12
|
import { researchPlatformCapabilities } from "./platform-capabilities.js";
|
|
10
13
|
import { configuredResearchSecrets, isSensitiveEnvironmentName, sanitizeResearchText, } from "./sanitization.js";
|
|
14
|
+
import { claudeCodeCompatibleSchema } from "./schema-compatibility.js";
|
|
11
15
|
import { isObject, sha256File, sha256Text } from "./storage.js";
|
|
12
16
|
const MAX_CAPTURE_BYTES = 5 * 1024 * 1024;
|
|
13
17
|
const MIN_CAPTURE_BYTES = 64 * 1024;
|
|
@@ -36,6 +40,7 @@ const CODEX_DISABLED_FEATURES = [
|
|
|
36
40
|
"tool_call_mcp_elicitation",
|
|
37
41
|
"tool_suggest",
|
|
38
42
|
"workspace_dependencies",
|
|
43
|
+
"view_image",
|
|
39
44
|
];
|
|
40
45
|
const ROUTE_AUTH_ENVIRONMENT = {
|
|
41
46
|
codex: ["OPENAI_API_KEY"],
|
|
@@ -48,11 +53,32 @@ const CLAUDE_SETTINGS_ENVIRONMENT = [
|
|
|
48
53
|
"ANTHROPIC_AUTH_TOKEN",
|
|
49
54
|
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
50
55
|
"ANTHROPIC_BASE_URL",
|
|
56
|
+
"ANTHROPIC_MODEL",
|
|
57
|
+
"ANTHROPIC_DEFAULT_FABLE_MODEL",
|
|
58
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
59
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
60
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
61
|
+
"ANTHROPIC_SMALL_FAST_MODEL",
|
|
62
|
+
"CLAUDE_CODE_SUBAGENT_MODEL",
|
|
51
63
|
];
|
|
52
64
|
const DEFAULT_AGENT_EFFORT = "low";
|
|
53
65
|
const DEFAULT_CODEX_VERBOSITY = "low";
|
|
54
66
|
export async function executeAgent(request) {
|
|
67
|
+
// Use the same configuration snapshot for admission and child execution.
|
|
68
|
+
request = { ...request, environment: { ...request.environment } };
|
|
55
69
|
requireHeadlessAgentRoute(request.route);
|
|
70
|
+
if ((request.toolPolicy === "packet-read" &&
|
|
71
|
+
(!isArtifactViewBinding(request.artifactViews) || request.brokerUrl !== null)) ||
|
|
72
|
+
(request.artifactViews && request.toolPolicy !== "packet-read")) {
|
|
73
|
+
throw new CliError("Packet-only review requires its exact artifact index and no broker route.", { code: "RESEARCH_ARTIFACT_VIEW_INVALID", exitCode: 3 });
|
|
74
|
+
}
|
|
75
|
+
const artifactSecrets = [
|
|
76
|
+
...configuredResearchSecrets(process.env),
|
|
77
|
+
...configuredResearchSecrets(request.environment),
|
|
78
|
+
];
|
|
79
|
+
const artifactViews = request.artifactViews
|
|
80
|
+
? await openArtifactViews(request.projectRoot, request.artifactViews.index, request.artifactViews.packetSha256, artifactSecrets)
|
|
81
|
+
: null;
|
|
56
82
|
validateAgentBinary(request.route);
|
|
57
83
|
if (!Number.isInteger(request.maxOutputTokens) || request.maxOutputTokens < 1) {
|
|
58
84
|
throw new CliError("Agent max output tokens must be a positive integer.", {
|
|
@@ -69,7 +95,9 @@ export async function executeAgent(request) {
|
|
|
69
95
|
const temporaryDirectory = join(request.capsuleRoot, "tmp");
|
|
70
96
|
await mkdir(temporaryDirectory, { recursive: true, mode: 0o700 });
|
|
71
97
|
const executables = await resolveAgentExecutables(request.route, request.environment.PATH);
|
|
72
|
-
const
|
|
98
|
+
const settingsEnvironment = await routeSettingsEnvironment(request.route, request.environment);
|
|
99
|
+
const providerRouting = configuredProviderRouting(request.route, request.environment, settingsEnvironment);
|
|
100
|
+
const runtime = await fingerprintResolvedBinary(request.route, executables.launcher, executables.target, request.environment.PATH, providerRouting);
|
|
73
101
|
if (request.expectedRuntime && !sameRuntimeFingerprint(runtime, request.expectedRuntime)) {
|
|
74
102
|
throw new CliError(`Configured ${request.route.agent} runtime drifted after the successful doctor smoke.`, {
|
|
75
103
|
code: "RESEARCH_EXECUTOR_DRIFT",
|
|
@@ -82,12 +110,15 @@ export async function executeAgent(request) {
|
|
|
82
110
|
actualWrapperSha256: runtime.wrapperSha256,
|
|
83
111
|
expectedAdapterSha256: request.expectedRuntime.adapterSha256,
|
|
84
112
|
actualAdapterSha256: runtime.adapterSha256,
|
|
113
|
+
expectedProviderRouting: request.expectedRuntime.providerRouting ?? null,
|
|
114
|
+
actualProviderRouting: runtime.providerRouting,
|
|
85
115
|
},
|
|
86
116
|
});
|
|
87
117
|
}
|
|
88
118
|
const capsuleHome = join(request.capsuleRoot, "home");
|
|
89
|
-
const capsuleAuth = await prepareCapsuleHome(request.route, capsuleHome, request.environment);
|
|
119
|
+
const capsuleAuth = await prepareCapsuleHome(request.route, capsuleHome, request.environment, settingsEnvironment);
|
|
90
120
|
const secrets = [...configuredResearchSecrets(request.environment), ...capsuleAuth.secrets];
|
|
121
|
+
artifactSecrets.push(...capsuleAuth.secrets);
|
|
91
122
|
if (request.route.agent === "codex") {
|
|
92
123
|
// Codex discovers project configuration by walking parent directories. Bound that
|
|
93
124
|
// discovery to the isolated capsule project so a host workspace .codex/config.toml
|
|
@@ -97,11 +128,14 @@ export async function executeAgent(request) {
|
|
|
97
128
|
mode: 0o600,
|
|
98
129
|
});
|
|
99
130
|
}
|
|
100
|
-
const invocation = await buildInvocation(request, executables.launcher, join(request.capsuleRoot, `${request.purpose}-output-schema.json`));
|
|
101
|
-
const sandboxed = await sandboxInvocation(invocation.binary, invocation.args, request.capsuleRoot, request.projectRoot, request.workspaceRoot, executables.target);
|
|
102
131
|
const started = process.hrtime.bigint();
|
|
103
132
|
let completed;
|
|
133
|
+
let sandboxed;
|
|
134
|
+
let artifactServer = null;
|
|
104
135
|
try {
|
|
136
|
+
artifactServer = artifactViews ? await startArtifactViewServer(artifactViews) : null;
|
|
137
|
+
const invocation = await buildInvocation(request, executables.launcher, join(request.capsuleRoot, `${request.purpose}-output-schema.json`), artifactServer?.url);
|
|
138
|
+
sandboxed = await sandboxInvocation(invocation.binary, invocation.args, request.capsuleRoot, request.projectRoot, request.workspaceRoot, executables.target);
|
|
105
139
|
completed = await spawnCaptured({
|
|
106
140
|
binary: sandboxed.binary,
|
|
107
141
|
args: sandboxed.args,
|
|
@@ -109,6 +143,7 @@ export async function executeAgent(request) {
|
|
|
109
143
|
cwd: request.projectRoot,
|
|
110
144
|
env: sanitizedEnvironment(request.environment, temporaryDirectory, capsuleHome, request.route, executables.target, capsuleAuth.environment),
|
|
111
145
|
timeoutMs: request.timeoutSeconds * 1000,
|
|
146
|
+
compactArtifactTrace: request.route.agent === "codex" && request.toolPolicy === "packet-read",
|
|
112
147
|
maxCaptureBytes: Math.min(MAX_CAPTURE_BYTES, Math.max(MIN_CAPTURE_BYTES, request.maxOutputTokens * BYTES_PER_OUTPUT_TOKEN +
|
|
113
148
|
(request.maxToolContextTokens ?? 0) * BYTES_PER_TOOL_CONTEXT_TOKEN)),
|
|
114
149
|
});
|
|
@@ -123,6 +158,8 @@ export async function executeAgent(request) {
|
|
|
123
158
|
throw error;
|
|
124
159
|
}
|
|
125
160
|
finally {
|
|
161
|
+
if (artifactServer)
|
|
162
|
+
await artifactServer.stop();
|
|
126
163
|
secrets.push(...(await reconcileCapsuleAuthentication(capsuleAuth)));
|
|
127
164
|
}
|
|
128
165
|
const wallSeconds = Number(process.hrtime.bigint() - started) / 1_000_000_000;
|
|
@@ -140,15 +177,21 @@ export async function executeAgent(request) {
|
|
|
140
177
|
}
|
|
141
178
|
const parsed = parseAgentResult(request.route, completed.stdout, completed.stderr);
|
|
142
179
|
const redacted = redactResult(parsed.stdout, parsed.stderr, secrets);
|
|
180
|
+
const toolPolicyViolated = request.toolPolicy === "packet-read"
|
|
181
|
+
? (parsed.telemetry.packetReadViolations ?? 0) > 0
|
|
182
|
+
: request.toolPolicy === "none" &&
|
|
183
|
+
(parsed.telemetry.toolCalls > 0 || (parsed.telemetry.packetReadViolations ?? 0) > 0);
|
|
143
184
|
const exitCode = redacted.exposed
|
|
144
185
|
? 86
|
|
145
186
|
: completed.captureExceeded
|
|
146
187
|
? 75
|
|
147
188
|
: completed.exitCode !== 0
|
|
148
189
|
? completed.exitCode
|
|
149
|
-
:
|
|
150
|
-
?
|
|
151
|
-
:
|
|
190
|
+
: toolPolicyViolated
|
|
191
|
+
? 78
|
|
192
|
+
: parsed.parseFailed
|
|
193
|
+
? 3
|
|
194
|
+
: 0;
|
|
152
195
|
const inputTokens = redacted.exposed ? 0 : parsed.inputTokens;
|
|
153
196
|
const cachedInputTokens = redacted.exposed ? 0 : parsed.cachedInputTokens;
|
|
154
197
|
const outputTokens = redacted.exposed ? 0 : parsed.outputTokens;
|
|
@@ -160,16 +203,19 @@ export async function executeAgent(request) {
|
|
|
160
203
|
stdout: redacted.stdout,
|
|
161
204
|
stderr: completed.captureExceeded
|
|
162
205
|
? `${redacted.stderr}\nagent output exceeded the configured capture limit`.trim()
|
|
163
|
-
:
|
|
206
|
+
: toolPolicyViolated
|
|
207
|
+
? `${redacted.stderr}\nreviewer tool policy violation: an unapproved I/O tool was reported`.trim()
|
|
208
|
+
: redacted.stderr,
|
|
164
209
|
tokens,
|
|
165
210
|
inputTokens,
|
|
166
211
|
cachedInputTokens,
|
|
167
212
|
outputTokens,
|
|
168
213
|
costUsd,
|
|
169
214
|
wallSeconds,
|
|
170
|
-
model:
|
|
171
|
-
runtime
|
|
172
|
-
...(request.toolPolicy === "none"
|
|
215
|
+
model: request.route.model,
|
|
216
|
+
runtime,
|
|
217
|
+
...((request.toolPolicy === "none" || request.toolPolicy === "packet-read") &&
|
|
218
|
+
request.brokerUrl === null
|
|
173
219
|
? {
|
|
174
220
|
isolation: {
|
|
175
221
|
...sandboxed.isolation,
|
|
@@ -179,12 +225,13 @@ export async function executeAgent(request) {
|
|
|
179
225
|
"private-capsule",
|
|
180
226
|
],
|
|
181
227
|
writeScopes: ["private-capsule"],
|
|
182
|
-
networkPolicy:
|
|
183
|
-
toolPolicy:
|
|
228
|
+
networkPolicy: reviewNetworkPolicy(request.toolPolicy),
|
|
229
|
+
toolPolicy: request.toolPolicy,
|
|
184
230
|
},
|
|
185
231
|
}
|
|
186
232
|
: {}),
|
|
187
|
-
telemetry: sanitizeExecutionTelemetry(parsed.telemetry, secrets),
|
|
233
|
+
telemetry: sanitizeExecutionTelemetry({ ...parsed.telemetry, reportedModel: parsed.model }, secrets),
|
|
234
|
+
...(artifactViews ? { artifactReads: artifactViews.receipts() } : {}),
|
|
188
235
|
};
|
|
189
236
|
}
|
|
190
237
|
function isNestedSandboxDenial(exitCode, stderr) {
|
|
@@ -196,9 +243,11 @@ function isNestedSandboxDenial(exitCode, stderr) {
|
|
|
196
243
|
}
|
|
197
244
|
export async function fingerprintAgentRoute(route, environment) {
|
|
198
245
|
requireHeadlessAgentRoute(route);
|
|
246
|
+
environment = { ...environment };
|
|
199
247
|
validateAgentBinary(route);
|
|
200
248
|
const executables = await resolveAgentExecutables(route, environment.PATH);
|
|
201
|
-
|
|
249
|
+
const settingsEnvironment = await routeSettingsEnvironment(route, environment);
|
|
250
|
+
return fingerprintResolvedBinary(route, executables.launcher, executables.target, environment.PATH, configuredProviderRouting(route, environment, settingsEnvironment));
|
|
202
251
|
}
|
|
203
252
|
export async function probeNativeCapsuleIsolation(input) {
|
|
204
253
|
const probeId = randomUUID();
|
|
@@ -296,8 +345,11 @@ export async function probeNativeCapsuleIsolation(input) {
|
|
|
296
345
|
]);
|
|
297
346
|
}
|
|
298
347
|
}
|
|
299
|
-
async function buildInvocation(request, resolvedBinary, outputSchemaPath) {
|
|
300
|
-
|
|
348
|
+
async function buildInvocation(request, resolvedBinary, outputSchemaPath, artifactServerUrl) {
|
|
349
|
+
const outputSchema = request.route.agent === "claude"
|
|
350
|
+
? claudeCodeCompatibleSchema(request.outputSchema)
|
|
351
|
+
: request.outputSchema;
|
|
352
|
+
await writeFile(outputSchemaPath, `${JSON.stringify(outputSchema, null, 2)}\n`, {
|
|
301
353
|
encoding: "utf8",
|
|
302
354
|
mode: 0o600,
|
|
303
355
|
});
|
|
@@ -324,7 +376,7 @@ async function buildInvocation(request, resolvedBinary, outputSchemaPath) {
|
|
|
324
376
|
];
|
|
325
377
|
for (const feature of CODEX_DISABLED_FEATURES)
|
|
326
378
|
args.push("--disable", feature);
|
|
327
|
-
if (toolPolicy
|
|
379
|
+
if (toolPolicy !== "workspace-read")
|
|
328
380
|
args.push("--disable", "shell_tool", "--disable", "unified_exec");
|
|
329
381
|
args.push("-c", "include_apps_instructions=false", "-c", "include_collaboration_mode_instructions=false", "-c", "include_environment_context=false", "-c", "include_permissions_instructions=false", "-c", `project_root_markers=[${JSON.stringify(CODEX_CAPSULE_ROOT_MARKER)}]`, "-c", `model_reasoning_effort=${JSON.stringify(request.route.effort ?? DEFAULT_AGENT_EFFORT)}`, "-c", `model_verbosity=${JSON.stringify(request.route.verbosity ?? DEFAULT_CODEX_VERBOSITY)}`);
|
|
330
382
|
if (request.brokerUrl) {
|
|
@@ -332,6 +384,8 @@ async function buildInvocation(request, resolvedBinary, outputSchemaPath) {
|
|
|
332
384
|
}
|
|
333
385
|
if (request.route.model)
|
|
334
386
|
args.push("--model", request.route.model);
|
|
387
|
+
if (artifactServerUrl)
|
|
388
|
+
args.push("-c", `mcp_servers.research_artifacts.url=${JSON.stringify(artifactServerUrl)}`, "-c", "mcp_servers.research_artifacts.required=true", "-c", `mcp_servers.research_artifacts.enabled_tools=${JSON.stringify(ARTIFACT_VIEW_TOOL_NAMES)}`);
|
|
335
389
|
return { binary: resolvedBinary, args, stdin: request.prompt };
|
|
336
390
|
}
|
|
337
391
|
if (request.route.agent !== "claude") {
|
|
@@ -355,14 +409,14 @@ async function buildInvocation(request, resolvedBinary, outputSchemaPath) {
|
|
|
355
409
|
"--no-chrome",
|
|
356
410
|
"--disable-slash-commands",
|
|
357
411
|
"--tools",
|
|
358
|
-
toolPolicy === "
|
|
412
|
+
toolPolicy === "workspace-read" ? "Read,Glob,Grep" : "",
|
|
359
413
|
"--effort",
|
|
360
414
|
request.route.effort ?? DEFAULT_AGENT_EFFORT,
|
|
361
415
|
];
|
|
362
416
|
if (request.purpose !== "repair") {
|
|
363
|
-
args.splice(1, 0, "--json-schema", JSON.stringify(
|
|
417
|
+
args.splice(1, 0, "--json-schema", JSON.stringify(outputSchema));
|
|
364
418
|
}
|
|
365
|
-
const allowedTools = toolPolicy === "
|
|
419
|
+
const allowedTools = toolPolicy === "workspace-read" ? ["Read", "Glob", "Grep"] : [];
|
|
366
420
|
if (request.brokerUrl) {
|
|
367
421
|
const mcpConfigPath = join(request.capsuleRoot, "research-mcp.json");
|
|
368
422
|
await writeFile(mcpConfigPath, `${JSON.stringify({
|
|
@@ -373,6 +427,16 @@ async function buildInvocation(request, resolvedBinary, outputSchemaPath) {
|
|
|
373
427
|
allowedTools.push("mcp__research_broker__fetch_candidate_source");
|
|
374
428
|
args.push("--strict-mcp-config", "--mcp-config", mcpConfigPath);
|
|
375
429
|
}
|
|
430
|
+
if (artifactServerUrl) {
|
|
431
|
+
const mcpConfigPath = join(request.capsuleRoot, "artifact-mcp.json");
|
|
432
|
+
await writeFile(mcpConfigPath, JSON.stringify({
|
|
433
|
+
mcpServers: {
|
|
434
|
+
research_artifacts: { type: "http", url: artifactServerUrl },
|
|
435
|
+
},
|
|
436
|
+
}), { encoding: "utf8", mode: 0o600 });
|
|
437
|
+
allowedTools.push(...ARTIFACT_VIEW_TOOL_NAMES.map((name) => `mcp__research_artifacts__${name}`));
|
|
438
|
+
args.push("--strict-mcp-config", "--mcp-config", mcpConfigPath);
|
|
439
|
+
}
|
|
376
440
|
args.push("--allowedTools", allowedTools.join(","));
|
|
377
441
|
if (request.maxCostUsd > 0)
|
|
378
442
|
args.push("--max-budget-usd", String(request.maxCostUsd));
|
|
@@ -385,8 +449,9 @@ function requireHeadlessAgentRoute(route) {
|
|
|
385
449
|
return;
|
|
386
450
|
throw new CliError(`The ${route.agent} producer is native-host only and cannot be launched by the control plane.`, { code: "RESEARCH_EXECUTOR_INVALID", exitCode: 2 });
|
|
387
451
|
}
|
|
388
|
-
async function sandboxInvocation(binary, args, capsuleRoot, projectRoot, workspaceRoot, targetBinary) {
|
|
452
|
+
async function sandboxInvocation(binary, args, capsuleRoot, projectRoot, workspaceRoot, targetBinary, mode = "review") {
|
|
389
453
|
const configuredCredentialPath = join(workspaceRoot, ".tiangong-research", ".env");
|
|
454
|
+
const workspaceReal = mode === "calculation" ? await realpath(workspaceRoot) : workspaceRoot;
|
|
390
455
|
const workspaceCredentialPath = await realpath(configuredCredentialPath).catch(() => configuredCredentialPath);
|
|
391
456
|
const capabilities = researchPlatformCapabilities(platform());
|
|
392
457
|
if (capabilities.nativeIsolationProvider === "sandbox-exec") {
|
|
@@ -400,9 +465,11 @@ async function sandboxInvocation(binary, args, capsuleRoot, projectRoot, workspa
|
|
|
400
465
|
"(version 1)",
|
|
401
466
|
"(deny default)",
|
|
402
467
|
"(allow process*)",
|
|
403
|
-
"(allow network*)",
|
|
468
|
+
...(mode === "review" ? ["(allow network*)"] : []),
|
|
404
469
|
"(allow file-read-metadata)",
|
|
405
|
-
|
|
470
|
+
mode === "calculation"
|
|
471
|
+
? `(allow file-read* (require-all (require-any ${readClauses} (literal "/") (literal "/var") (literal "/dev/dtracehelper") (literal "/dev/null") (literal "/dev/urandom")) (require-not (subpath ${sandboxString(workspaceReal)})) (require-not (literal ${sandboxString(workspaceCredentialPath)}))))`
|
|
472
|
+
: `(allow file-read* ${readClauses} (literal "/") (literal "/var") (literal "/dev/dtracehelper") (literal "/dev/null") (literal "/dev/urandom"))`,
|
|
406
473
|
`(deny file-read* (literal ${sandboxString(workspaceCredentialPath)}))`,
|
|
407
474
|
'(allow file-ioctl (literal "/dev/dtracehelper"))',
|
|
408
475
|
"(allow sysctl-read)",
|
|
@@ -430,7 +497,13 @@ async function sandboxInvocation(binary, args, capsuleRoot, projectRoot, workspa
|
|
|
430
497
|
exitCode: 3,
|
|
431
498
|
});
|
|
432
499
|
}
|
|
500
|
+
// Review keeps its original argument order verbatim (the credential
|
|
501
|
+
// /dev/null mask must stay AFTER the capsule bind, or a capsule containing
|
|
502
|
+
// the credential path would re-expose it and shift the policy hash);
|
|
503
|
+
// calculation-only branches append namespaces and the workspace mask.
|
|
433
504
|
const sandboxArgs = ["--die-with-parent", "--new-session"];
|
|
505
|
+
if (mode === "calculation")
|
|
506
|
+
sandboxArgs.push("--unshare-net", "--unshare-pid");
|
|
434
507
|
const systemRoots = await existingLinuxSystemRoots();
|
|
435
508
|
for (const path of systemRoots) {
|
|
436
509
|
sandboxArgs.push("--ro-bind", path, path);
|
|
@@ -447,12 +520,22 @@ async function sandboxInvocation(binary, args, capsuleRoot, projectRoot, workspa
|
|
|
447
520
|
continue;
|
|
448
521
|
}
|
|
449
522
|
await appendBubblewrapParentDirectories(sandboxArgs, runtimeRoot, createdDirectories);
|
|
523
|
+
// Bound unconditionally: when the workspace sits beneath a runtime root,
|
|
524
|
+
// the calculation-mode workspace mask below shadows only that subtree
|
|
525
|
+
// and the rest of the runtime stays available.
|
|
450
526
|
sandboxArgs.push("--ro-bind", runtimeRoot, runtimeRoot);
|
|
451
527
|
createdDirectories.add(runtimeRoot);
|
|
452
528
|
}
|
|
529
|
+
if (mode === "calculation") {
|
|
530
|
+
// A private read-only tmpfs hides workspace data even beneath an allowed
|
|
531
|
+
// runtime/system root. No writable host alias can populate this mask.
|
|
532
|
+
await appendBubblewrapParentDirectories(sandboxArgs, workspaceReal, createdDirectories);
|
|
533
|
+
sandboxArgs.push("--tmpfs", workspaceReal, "--remount-ro", workspaceReal);
|
|
534
|
+
}
|
|
453
535
|
await appendBubblewrapParentDirectories(sandboxArgs, capsuleRoot, createdDirectories);
|
|
454
536
|
sandboxArgs.push("--bind", capsuleRoot, capsuleRoot, "--proc", "/proc", "--dev", "/dev", "--chdir", projectRoot);
|
|
455
|
-
if (
|
|
537
|
+
if (mode === "review" &&
|
|
538
|
+
workspaceCredentialPath.startsWith(`${capsuleRoot}/`) &&
|
|
456
539
|
(await pathIsReadable(workspaceCredentialPath))) {
|
|
457
540
|
sandboxArgs.push("--ro-bind", "/dev/null", workspaceCredentialPath);
|
|
458
541
|
}
|
|
@@ -476,7 +559,7 @@ async function sandboxInvocation(binary, args, capsuleRoot, projectRoot, workspa
|
|
|
476
559
|
},
|
|
477
560
|
});
|
|
478
561
|
}
|
|
479
|
-
async function prepareCapsuleHome(route, capsuleHome, environment) {
|
|
562
|
+
async function prepareCapsuleHome(route, capsuleHome, environment, settingsEnvironment) {
|
|
480
563
|
await mkdir(capsuleHome, { recursive: true, mode: 0o700 });
|
|
481
564
|
const sourceHome = environment.HOME && isAbsolute(environment.HOME) ? environment.HOME : homedir();
|
|
482
565
|
const claudeConfigRoot = environment.CLAUDE_CONFIG_DIR && isAbsolute(environment.CLAUDE_CONFIG_DIR)
|
|
@@ -522,9 +605,6 @@ async function prepareCapsuleHome(route, capsuleHome, environment) {
|
|
|
522
605
|
});
|
|
523
606
|
secrets.push(...authSecretValues(await readFile(candidate.destination, "utf8")));
|
|
524
607
|
}
|
|
525
|
-
const settingsEnvironment = route.agent === "claude"
|
|
526
|
-
? await readClaudeSettingsEnvironment(join(claudeConfigRoot, "settings.json"))
|
|
527
|
-
: {};
|
|
528
608
|
for (const name of ROUTE_AUTH_ENVIRONMENT[route.agent]) {
|
|
529
609
|
const value = settingsEnvironment[name];
|
|
530
610
|
if (value && value.length >= 8)
|
|
@@ -658,6 +738,88 @@ function authenticationReconciliationError(reason) {
|
|
|
658
738
|
},
|
|
659
739
|
});
|
|
660
740
|
}
|
|
741
|
+
async function routeSettingsEnvironment(route, environment) {
|
|
742
|
+
if (route.agent !== "claude")
|
|
743
|
+
return {};
|
|
744
|
+
const sourceHome = environment.HOME && isAbsolute(environment.HOME) ? environment.HOME : homedir();
|
|
745
|
+
const configRoot = environment.CLAUDE_CONFIG_DIR && isAbsolute(environment.CLAUDE_CONFIG_DIR)
|
|
746
|
+
? environment.CLAUDE_CONFIG_DIR
|
|
747
|
+
: join(sourceHome, ".claude");
|
|
748
|
+
return readClaudeSettingsEnvironment(join(configRoot, "settings.json"));
|
|
749
|
+
}
|
|
750
|
+
function configuredProviderRouting(route, environment, settings) {
|
|
751
|
+
// This is a binding of admitted configuration, never a wire-level identity claim.
|
|
752
|
+
// Keep URL paths/queries, proxy credentials and mapped provider IDs out of receipts.
|
|
753
|
+
const selected = new Map();
|
|
754
|
+
const modelMappingSources = {};
|
|
755
|
+
const names = new Set([...Object.keys(environment), ...Object.keys(settings)]);
|
|
756
|
+
for (const name of [...names].sort()) {
|
|
757
|
+
if (isSensitiveEnvironmentName(name))
|
|
758
|
+
continue;
|
|
759
|
+
const isModel = route.agent === "claude" &&
|
|
760
|
+
(/^ANTHROPIC_(?:DEFAULT_[A-Z_]+_MODEL|MODEL|SMALL_FAST_MODEL)$/.test(name) ||
|
|
761
|
+
name === "CLAUDE_CODE_SUBAGENT_MODEL");
|
|
762
|
+
const isRouting = /^(?:https?_proxy|all_proxy|no_proxy)$/i.test(name) ||
|
|
763
|
+
(route.agent === "claude" &&
|
|
764
|
+
(/^ANTHROPIC_[A-Z_]*BASE_URL$/.test(name) || /^CLAUDE_CODE_USE_[A-Z_]+$/.test(name))) ||
|
|
765
|
+
(route.agent === "codex" && name === "OPENAI_BASE_URL");
|
|
766
|
+
if (!isModel && !isRouting)
|
|
767
|
+
continue;
|
|
768
|
+
const value = environment[name] || settings[name];
|
|
769
|
+
if (!value)
|
|
770
|
+
continue;
|
|
771
|
+
selected.set(name, value);
|
|
772
|
+
if (isModel)
|
|
773
|
+
modelMappingSources[name] = environment[name] ? "process-environment" : "claude-settings-env";
|
|
774
|
+
}
|
|
775
|
+
const endpoint = route.agent === "claude" ? selected.get("ANTHROPIC_BASE_URL") : undefined;
|
|
776
|
+
if (endpoint)
|
|
777
|
+
assertSafeClaudeBaseUrl(endpoint);
|
|
778
|
+
return {
|
|
779
|
+
schemaVersion: 1,
|
|
780
|
+
configurationSha256: sha256Text(JSON.stringify([...selected])),
|
|
781
|
+
endpointOrigin: endpoint ? new URL(endpoint).origin : null,
|
|
782
|
+
endpointSource: endpoint
|
|
783
|
+
? environment.ANTHROPIC_BASE_URL
|
|
784
|
+
? "process-environment"
|
|
785
|
+
: "claude-settings-env"
|
|
786
|
+
: "runtime-default",
|
|
787
|
+
modelMappingSources,
|
|
788
|
+
identityVerification: "unverified",
|
|
789
|
+
scope: "configured-routing-only",
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
/** Offline configuration inspection; never invokes an agent or sends material. */
|
|
793
|
+
export async function inspectAgentProviderRouting(route, environment) {
|
|
794
|
+
environment = { ...environment };
|
|
795
|
+
return configuredProviderRouting(route, environment, await routeSettingsEnvironment(route, environment));
|
|
796
|
+
}
|
|
797
|
+
export function isAgentProviderRouting(value) {
|
|
798
|
+
if (!isObject(value) ||
|
|
799
|
+
value.schemaVersion !== 1 ||
|
|
800
|
+
typeof value.configurationSha256 !== "string" ||
|
|
801
|
+
!/^[0-9a-f]{64}$/.test(value.configurationSha256) ||
|
|
802
|
+
typeof value.endpointSource !== "string" ||
|
|
803
|
+
!["process-environment", "claude-settings-env", "runtime-default"].includes(value.endpointSource) ||
|
|
804
|
+
value.identityVerification !== "unverified" ||
|
|
805
|
+
value.scope !== "configured-routing-only" ||
|
|
806
|
+
!isObject(value.modelMappingSources))
|
|
807
|
+
return false;
|
|
808
|
+
if (value.endpointOrigin !== null) {
|
|
809
|
+
if (typeof value.endpointOrigin !== "string")
|
|
810
|
+
return false;
|
|
811
|
+
try {
|
|
812
|
+
const url = new URL(value.endpointOrigin);
|
|
813
|
+
if (url.protocol !== "https:" || url.origin !== value.endpointOrigin)
|
|
814
|
+
return false;
|
|
815
|
+
}
|
|
816
|
+
catch {
|
|
817
|
+
return false;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
return Object.entries(value.modelMappingSources).every(([name, source]) => /^(?:ANTHROPIC_(?:DEFAULT_[A-Z_]+_MODEL|MODEL|SMALL_FAST_MODEL)|CLAUDE_CODE_SUBAGENT_MODEL)$/.test(name) &&
|
|
821
|
+
(source === "process-environment" || source === "claude-settings-env"));
|
|
822
|
+
}
|
|
661
823
|
async function readClaudeSettingsEnvironment(path) {
|
|
662
824
|
const info = await lstat(path).catch(() => undefined);
|
|
663
825
|
if (!info)
|
|
@@ -732,7 +894,7 @@ function assertOwnerOnlyAuthenticationFile(mode, agent) {
|
|
|
732
894
|
});
|
|
733
895
|
}
|
|
734
896
|
}
|
|
735
|
-
async function fingerprintResolvedBinary(route, launcher, target, pathValue) {
|
|
897
|
+
async function fingerprintResolvedBinary(route, launcher, target, pathValue, providerRouting) {
|
|
736
898
|
const versionEnvironment = {
|
|
737
899
|
PATH: pathValue ?? process.env.PATH,
|
|
738
900
|
NO_COLOR: "1",
|
|
@@ -773,6 +935,7 @@ async function fingerprintResolvedBinary(route, launcher, target, pathValue) {
|
|
|
773
935
|
binaryVersion: sanitizeResearchText(binaryVersion).slice(0, 300),
|
|
774
936
|
platform: platform(),
|
|
775
937
|
architecture: arch(),
|
|
938
|
+
providerRouting,
|
|
776
939
|
};
|
|
777
940
|
}
|
|
778
941
|
async function resolveAgentExecutables(route, pathValue) {
|
|
@@ -858,7 +1021,17 @@ async function spawnCaptured(input) {
|
|
|
858
1021
|
}
|
|
859
1022
|
target.push(chunk);
|
|
860
1023
|
};
|
|
861
|
-
|
|
1024
|
+
const artifactTrace = input.compactArtifactTrace
|
|
1025
|
+
? codexArtifactTrace({
|
|
1026
|
+
maxControlBytes: input.maxCaptureBytes,
|
|
1027
|
+
line: (chunk) => capture(stdout, chunk),
|
|
1028
|
+
oversized: () => {
|
|
1029
|
+
captureExceeded = true;
|
|
1030
|
+
terminate();
|
|
1031
|
+
},
|
|
1032
|
+
})
|
|
1033
|
+
: null;
|
|
1034
|
+
child.stdout.on("data", (chunk) => artifactTrace ? artifactTrace.write(chunk) : capture(stdout, chunk));
|
|
862
1035
|
child.stderr.on("data", (chunk) => capture(stderr, chunk));
|
|
863
1036
|
child.on("error", (error) => {
|
|
864
1037
|
if (settled)
|
|
@@ -870,6 +1043,7 @@ async function spawnCaptured(input) {
|
|
|
870
1043
|
child.on("close", (code, signal) => {
|
|
871
1044
|
if (settled)
|
|
872
1045
|
return;
|
|
1046
|
+
artifactTrace?.end();
|
|
873
1047
|
settled = true;
|
|
874
1048
|
clearTimeout(timer);
|
|
875
1049
|
const detail = signal ? `\nprocess terminated by ${signal}` : "";
|
|
@@ -887,12 +1061,13 @@ function parseAgentResult(route, stdout, stderr) {
|
|
|
887
1061
|
let inputTokens = 0;
|
|
888
1062
|
let cachedInputTokens = 0;
|
|
889
1063
|
let outputTokens = 0;
|
|
890
|
-
let model =
|
|
1064
|
+
let model = null;
|
|
891
1065
|
const messages = [];
|
|
892
1066
|
let parsedEvents = 0;
|
|
893
1067
|
const eventCounts = {};
|
|
894
1068
|
const itemCounts = {};
|
|
895
1069
|
let toolCalls = 0;
|
|
1070
|
+
let packetReadViolations = 0;
|
|
896
1071
|
let reasoningOutputTokens = 0;
|
|
897
1072
|
const providerErrors = [];
|
|
898
1073
|
for (const line of stdout.split(/\r?\n/)) {
|
|
@@ -918,6 +1093,9 @@ function parseAgentResult(route, stdout, stderr) {
|
|
|
918
1093
|
incrementCount(itemCounts, event.item.type);
|
|
919
1094
|
if (isToolItemType(event.item.type))
|
|
920
1095
|
toolCalls += 1;
|
|
1096
|
+
if (event.item.type === "file_change" ||
|
|
1097
|
+
(isToolItemType(event.item.type) && !isPacketArtifactTool(event.item)))
|
|
1098
|
+
packetReadViolations += 1;
|
|
921
1099
|
if (event.item.type === "error")
|
|
922
1100
|
appendProviderError(providerErrors, event.item);
|
|
923
1101
|
}
|
|
@@ -943,6 +1121,7 @@ function parseAgentResult(route, stdout, stderr) {
|
|
|
943
1121
|
eventCounts,
|
|
944
1122
|
itemCounts,
|
|
945
1123
|
toolCalls,
|
|
1124
|
+
...(packetReadViolations ? { packetReadViolations } : {}),
|
|
946
1125
|
providerTurns: eventCounts["turn.completed"] ?? eventCounts["turn.started"] ?? null,
|
|
947
1126
|
reasoningOutputTokens,
|
|
948
1127
|
providerErrors,
|
|
@@ -952,22 +1131,40 @@ function parseAgentResult(route, stdout, stderr) {
|
|
|
952
1131
|
try {
|
|
953
1132
|
const value = JSON.parse(stdout);
|
|
954
1133
|
const usage = isObject(value.usage) ? value.usage : {};
|
|
1134
|
+
const providerFailed = value.is_error === true ||
|
|
1135
|
+
(typeof value.subtype === "string" && value.subtype.startsWith("error_"));
|
|
1136
|
+
const providerErrors = [];
|
|
1137
|
+
if (providerFailed) {
|
|
1138
|
+
if (typeof value.subtype === "string")
|
|
1139
|
+
appendProviderError(providerErrors, { message: value.subtype });
|
|
1140
|
+
if (Array.isArray(value.errors)) {
|
|
1141
|
+
for (const error of value.errors)
|
|
1142
|
+
if (typeof error === "string")
|
|
1143
|
+
appendProviderError(providerErrors, { message: error });
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
const structured = value.structured_output !== undefined && value.structured_output !== null;
|
|
1147
|
+
const answer = structured
|
|
1148
|
+
? JSON.stringify(value.structured_output)
|
|
1149
|
+
: typeof value.result === "string"
|
|
1150
|
+
? value.result
|
|
1151
|
+
: stdout;
|
|
955
1152
|
return {
|
|
956
|
-
stdout:
|
|
1153
|
+
stdout: providerFailed ? "" : answer,
|
|
957
1154
|
stderr,
|
|
958
1155
|
inputTokens: numeric(usage.input_tokens),
|
|
959
1156
|
cachedInputTokens: numeric(usage.cache_creation_input_tokens) + numeric(usage.cache_read_input_tokens),
|
|
960
1157
|
outputTokens: numeric(usage.output_tokens),
|
|
961
1158
|
costUsd: numeric(value.total_cost_usd),
|
|
962
|
-
model: typeof value.model === "string" ? value.model :
|
|
963
|
-
parseFailed: typeof value.result !== "string",
|
|
1159
|
+
model: typeof value.model === "string" ? value.model : null,
|
|
1160
|
+
parseFailed: providerFailed || (!structured && typeof value.result !== "string"),
|
|
964
1161
|
telemetry: {
|
|
965
1162
|
eventCounts: { result: 1 },
|
|
966
1163
|
itemCounts: {},
|
|
967
1164
|
toolCalls: 0,
|
|
968
1165
|
providerTurns: numeric(value.num_turns) || null,
|
|
969
1166
|
reasoningOutputTokens: numeric(usage.reasoning_output_tokens),
|
|
970
|
-
providerErrors
|
|
1167
|
+
providerErrors,
|
|
971
1168
|
},
|
|
972
1169
|
};
|
|
973
1170
|
}
|
|
@@ -979,7 +1176,7 @@ function parseAgentResult(route, stdout, stderr) {
|
|
|
979
1176
|
cachedInputTokens: 0,
|
|
980
1177
|
outputTokens: 0,
|
|
981
1178
|
costUsd: 0,
|
|
982
|
-
model:
|
|
1179
|
+
model: null,
|
|
983
1180
|
parseFailed: true,
|
|
984
1181
|
telemetry: {
|
|
985
1182
|
eventCounts: {},
|
|
@@ -1213,13 +1410,21 @@ function appendProviderError(target, value) {
|
|
|
1213
1410
|
.find(Boolean);
|
|
1214
1411
|
if (!candidate)
|
|
1215
1412
|
return;
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1413
|
+
// Truncate only after sanitization; otherwise a boundary can leave part of a
|
|
1414
|
+
// configured secret unmatched by the later exact-value redactor.
|
|
1415
|
+
if (!target.includes(candidate))
|
|
1416
|
+
target.push(candidate);
|
|
1219
1417
|
}
|
|
1220
1418
|
function sanitizeExecutionTelemetry(telemetry, secrets) {
|
|
1221
1419
|
return {
|
|
1222
1420
|
...telemetry,
|
|
1421
|
+
...(telemetry.reportedModel === undefined
|
|
1422
|
+
? {}
|
|
1423
|
+
: {
|
|
1424
|
+
reportedModel: telemetry.reportedModel === null
|
|
1425
|
+
? null
|
|
1426
|
+
: sanitizeResearchText(telemetry.reportedModel, secrets).slice(0, 300),
|
|
1427
|
+
}),
|
|
1223
1428
|
providerErrors: telemetry.providerErrors.map((error) => sanitizeResearchText(error, secrets).slice(0, 1_000)),
|
|
1224
1429
|
};
|
|
1225
1430
|
}
|
|
@@ -1231,7 +1436,7 @@ function isToolItemType(value) {
|
|
|
1231
1436
|
value === "tool_call" ||
|
|
1232
1437
|
value.endsWith("_tool_call"));
|
|
1233
1438
|
}
|
|
1234
|
-
function sameRuntimeFingerprint(actual, expected) {
|
|
1439
|
+
export function sameRuntimeFingerprint(actual, expected) {
|
|
1235
1440
|
return (actual.agent === expected.agent &&
|
|
1236
1441
|
actual.model === expected.model &&
|
|
1237
1442
|
actual.effort === expected.effort &&
|
|
@@ -1241,6 +1446,45 @@ function sameRuntimeFingerprint(actual, expected) {
|
|
|
1241
1446
|
actual.adapterSha256 === expected.adapterSha256 &&
|
|
1242
1447
|
actual.binaryVersion === expected.binaryVersion &&
|
|
1243
1448
|
actual.platform === expected.platform &&
|
|
1244
|
-
actual.architecture === expected.architecture
|
|
1449
|
+
actual.architecture === expected.architecture &&
|
|
1450
|
+
actual.providerRouting?.configurationSha256 === expected.providerRouting?.configurationSha256);
|
|
1451
|
+
}
|
|
1452
|
+
function calculationUnsupported(detail) {
|
|
1453
|
+
return new CliError(`This platform cannot enforce the calculation boundary: ${detail}`, {
|
|
1454
|
+
code: "RESEARCH_SANDBOX_UNAVAILABLE",
|
|
1455
|
+
exitCode: 3,
|
|
1456
|
+
});
|
|
1457
|
+
}
|
|
1458
|
+
function covers(child, parent) {
|
|
1459
|
+
return (child === parent || (parent === "/" ? child.startsWith("/") : child.startsWith(`${parent}/`)));
|
|
1460
|
+
}
|
|
1461
|
+
/**
|
|
1462
|
+
* Build the confinement for one observed calculation attempt. The capsule is
|
|
1463
|
+
* the only writable/readable project surface, runtime and system roots are
|
|
1464
|
+
* read-only, and the workspace tree (data, holdout, external outputs) plus
|
|
1465
|
+
* host network and host process view are denied. Unsupported platforms and
|
|
1466
|
+
* unsupported overlaps are refused before execution; no raw fallback exists.
|
|
1467
|
+
*/
|
|
1468
|
+
export async function createCalculationSandboxInvocation(input) {
|
|
1469
|
+
const provider = researchPlatformCapabilities(platform()).nativeIsolationProvider;
|
|
1470
|
+
if (provider !== "sandbox-exec" && provider !== "bubblewrap")
|
|
1471
|
+
throw calculationUnsupported("no sandbox-exec or Bubblewrap provider on this platform");
|
|
1472
|
+
const capsuleRoot = await realpath(input.capsuleRoot).catch(() => {
|
|
1473
|
+
throw calculationUnsupported("capsule root is missing or symbolic");
|
|
1474
|
+
});
|
|
1475
|
+
const workspaceRoot = (await realpath(input.workspaceRoot).catch(() => undefined)) ?? resolve(input.workspaceRoot);
|
|
1476
|
+
const binary = await realpath(input.binary).catch(() => {
|
|
1477
|
+
throw calculationUnsupported("calculation runtime is missing or symbolic");
|
|
1478
|
+
});
|
|
1479
|
+
if (!(await lstat(binary)).isFile())
|
|
1480
|
+
throw calculationUnsupported("calculation runtime is not a regular file");
|
|
1481
|
+
const runtimeRoot = executableReadRoot(binary);
|
|
1482
|
+
if (runtimeRoot === "/" || covers("/", runtimeRoot))
|
|
1483
|
+
throw calculationUnsupported("runtime read root would expose the whole filesystem");
|
|
1484
|
+
if (covers(runtimeRoot, workspaceRoot))
|
|
1485
|
+
throw calculationUnsupported("runtime distribution lives inside the denied workspace tree; masking it would break the runtime");
|
|
1486
|
+
if (covers(capsuleRoot, workspaceRoot) || covers(workspaceRoot, capsuleRoot))
|
|
1487
|
+
throw calculationUnsupported("use a scratch capsule outside the research workspace tree");
|
|
1488
|
+
return sandboxInvocation(binary, input.args, capsuleRoot, capsuleRoot, workspaceRoot, binary, "calculation");
|
|
1245
1489
|
}
|
|
1246
1490
|
//# sourceMappingURL=executor.js.map
|