Repository Public
1# Shell23> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.45The shell tool gives models the ability to work inside a complete terminal environment. We support shell for local execution and for hosted execution through the Responses API.67The shell tool lets models run commands through either:89- Hosted shell containers managed by OpenAI.10- [A local shell runtime](#local-shell-mode) that you host and execute yourself.1112Shell is available through the [Responses API](https://developers.openai.com/api/docs/guides/migrate-to-responses). It's not available via the Chat Completions API.1314Running arbitrary shell commands can be dangerous. Always sandbox execution,15 apply allowlists or denylists where possible, and log tool activity for16 auditing.1718## Hosted shell quickstart1920Hosted shell is a native and streamlined option for tasks that need richer, deterministic processing, from running calculations to working with multimedia.2122Use `container_auto` when you want OpenAI to provision and manage a container for the request.2324Shell tool with container_auto2526```bash27curl -L 'https://api.openai.com/v1/responses' \28 -H "Content-Type: application/json" \29 -H "Authorization: Bearer $OPENAI_API_KEY" \30 -d '{31 "model": "gpt-6-astra",32 "tools": [33 { "type": "shell", "environment": { "type": "container_auto" } }34 ],35 "input": [36 {37 "type": "message",38 "role": "user",39 "content": [40 { "type": "input_text", "text": "Execute: ls -lah /mnt/data && python --version && node --version" }41 ]42 }43 ],44 "tool_choice": "auto"45 }'46```4748```javascript49import OpenAI from "openai";5051const client = new OpenAI();5253const response = await client.responses.create({54 model: "gpt-6-astra",55 tools: [{ type: "shell", environment: { type: "container_auto" } }],56 input: [57 {58 type: "message",59 role: "user",60 content: [61 {62 type: "input_text",63 text: "Execute: ls -lah /mnt/data && python --version && node --version",64 },65 ],66 },67 ],68 tool_choice: "auto",69});7071console.log(response.output_text);72```7374```python75from openai import OpenAI7677client = OpenAI()7879response = client.responses.create(80 model="gpt-6-astra",81 tools=[{"type": "shell", "environment": {"type": "container_auto"}}],82 input=[83 {84 "type": "message",85 "role": "user",86 "content": [87 {88 "type": "input_text",89 "text": "Execute: ls -lah /mnt/data && python --version && node --version",90 }91 ],92 }93 ],94 tool_choice="auto",95)9697print(response.output_text)98```99100```go101package main102103import (104 "context"105 "fmt"106107 "github.com/openai/openai-go/v3"108 "github.com/openai/openai-go/v3/responses"109)110111func main() {112 client := openai.NewClient()113 tool := responses.ToolUnionParam{OfShell: &responses.FunctionShellToolParam{114 Environment: responses.FunctionShellToolEnvironmentUnionParam{OfContainerAuto: &responses.ContainerAutoParam{}},115 }}116 response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{117 Model: "gpt-6-astra",118 Tools: []responses.ToolUnionParam{tool},119 Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Execute: ls -lah /mnt/data && python --version && node --version")},120 })121 if err != nil {122 panic(err)123 }124 fmt.Println(response.OutputText())125}126```127128```java129import com.openai.client.OpenAIClient;130import com.openai.client.okhttp.OpenAIOkHttpClient;131import com.openai.models.responses.ContainerAuto;132import com.openai.models.responses.FunctionShellTool;133import com.openai.models.responses.ResponseCreateParams;134135ResponseCreateParams params =136 ResponseCreateParams.builder()137 .model("gpt-6-astra")138 .input("Run ls -lah /mnt/data, then show the Python and Node.js versions.")139 .addTool(140 FunctionShellTool.builder().environment(ContainerAuto.builder().build()).build())141 .build();142143client.responses().create(params).output().stream()144 .flatMap(item -> item.message().stream())145 .flatMap(message -> message.content().stream())146 .flatMap(content -> content.outputText().stream())147 .forEach(text -> System.out.println(text.text()));148```149150```ruby151require "openai"152153client = OpenAI::Client.new154response = client.responses.create(155 model: "gpt-6-astra",156 input: "Run ls -lah /mnt/data, then show the Python and Node.js versions.",157 tools: [158 {159 type: :shell,160 environment: { type: :container_auto }161 }162 ]163)164165puts(response.output_text)166```167168169## Hosted runtime details170171- Runtime is currently based on `Debian 12` and may change over time.172- Default working directory is `/mnt/data`.173- `/mnt/data` is always present and is the supported path for user-downloadable artifacts.174- Hosted shell doesn't support interactive TTY sessions.175- Hosted shell commands don't run with `sudo`.176- You can run services inside the container when your workflow needs them.177178Current preinstalled languages include:179180- Python `3.11`181- Node.js `22.16`182- Java `17.0`183- PHP `8.2`184- Ruby `3.1`185- Go `1.23`186187## Reuse a container across requests188189If you need a long-running environment for iterative workflows, create a container and then reference it in subsequent Responses API calls.190191### 1. Create a container192193Create a reusable container194195```bash196curl -L 'https://api.openai.com/v1/containers' \197 -H "Content-Type: application/json" \198 -H "Authorization: Bearer $OPENAI_API_KEY" \199 -d '{200 "name": "analysis-container",201 "memory_limit": "1g",202 "expires_after": { "anchor": "last_active_at", "minutes": 20 }203 }'204```205206```javascript207import OpenAI from "openai";208209const client = new OpenAI();210211const container = await client.containers.create({212 name: "analysis-container",213 memory_limit: "1g",214 expires_after: { anchor: "last_active_at", minutes: 20 },215});216217console.log(container.id);218```219220```python221from openai import OpenAI222223client = OpenAI()224225container = client.containers.create(226 name="analysis-container",227 memory_limit="1g",228 expires_after={"anchor": "last_active_at", "minutes": 20},229)230231print(container.id)232```233234```go235package main236237import (238 "context"239 "fmt"240241 "github.com/openai/openai-go/v3"242)243244func main() {245 client := openai.NewClient()246 container, err := client.Containers.New(context.Background(), openai.ContainerNewParams{247 Name: "analysis-container",248 MemoryLimit: openai.ContainerNewParamsMemoryLimit1g,249 ExpiresAfter: openai.ContainerNewParamsExpiresAfter{250 Anchor: "last_active_at",251 Minutes: 20,252 },253 })254 if err != nil {255 panic(err)256 }257 fmt.Println(container.ID)258}259```260261```java262import com.openai.client.OpenAIClient;263import com.openai.client.okhttp.OpenAIOkHttpClient;264import com.openai.models.containers.ContainerCreateParams;265266var container =267 client268 .containers()269 .create(270 ContainerCreateParams.builder()271 .name("analysis")272 .expiresAfter(273 ContainerCreateParams.ExpiresAfter.builder()274 .anchor(ContainerCreateParams.ExpiresAfter.Anchor.LAST_ACTIVE_AT)275 .minutes(20)276 .build())277 .build());278279System.out.println(container.id());280```281282```ruby283require "openai"284285client = OpenAI::Client.new286container = client.containers.create(287 name: "analysis", expires_after: {288 anchor: :last_active_at,289 minutes: 20290 }291)292puts(container.id)293```294295296### 2. Reference the container in Responses297298Use shell with container_reference299300```bash301curl -L 'https://api.openai.com/v1/responses' \302 -H "Content-Type: application/json" \303 -H "Authorization: Bearer $OPENAI_API_KEY" \304 -d '{305 "model": "gpt-6-astra",306 "tools": [307 {308 "type": "shell",309 "environment": {310 "type": "container_reference",311 "container_id": "cntr_08f3d96c87a585390069118b594f7481a088b16cda7d9415fe"312 }313 }314 ],315 "input": "List files in the container and show disk usage."316 }'317```318319```javascript320import OpenAI from "openai";321322const client = new OpenAI();323324const response = await client.responses.create({325 model: "gpt-6-astra",326 tools: [327 {328 type: "shell",329 environment: {330 type: "container_reference",331 container_id: "cntr_08f3d96c87a585390069118b594f7481a088b16cda7d9415fe",332 },333 },334 ],335 input: "List files in the container and show disk usage.",336});337338console.log(response.output_text);339```340341```python342response = client.responses.create(343 model="gpt-6-astra",344 tools=[345 {346 "type": "shell",347 "environment": {348 "type": "container_reference",349 "container_id": container.id,350 },351 }352 ],353 input="List files in the container and show disk usage.",354)355356print(response.output_text)357```358359```go360package main361362import (363 "context"364 "fmt"365366 "github.com/openai/openai-go/v3"367 "github.com/openai/openai-go/v3/responses"368)369370func main() {371 client := openai.NewClient()372 tool := responses.ToolUnionParam{OfShell: &responses.FunctionShellToolParam{373 Environment: responses.FunctionShellToolEnvironmentUnionParam{OfContainerReference: &responses.ContainerReferenceParam{ContainerID: "cntr_08f3d96c87a585390069118b594f7481a088b16cda7d9415fe"}},374 }}375 response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{376 Model: "gpt-6-astra",377 Tools: []responses.ToolUnionParam{tool},378 Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("List files in the container and show disk usage.")},379 })380 if err != nil {381 panic(err)382 }383 fmt.Println(response.OutputText())384}385```386387```java388import com.openai.client.OpenAIClient;389import com.openai.client.okhttp.OpenAIOkHttpClient;390import com.openai.models.responses.FunctionShellTool;391import com.openai.models.responses.ResponseCreateParams;392393String containerId = "cntr_08f3d96c87a585390069118b594f7481a088b16cda7d9415fe";394395ResponseCreateParams params =396 ResponseCreateParams.builder()397 .model("gpt-6-astra")398 .input("List files in the container and show disk usage.")399 .addTool(FunctionShellTool.builder().containerReferenceEnvironment(containerId).build())400 .build();401402client.responses().create(params).output().stream()403 .flatMap(item -> item.message().stream())404 .flatMap(message -> message.content().stream())405 .flatMap(content -> content.outputText().stream())406 .forEach(text -> System.out.println(text.text()));407```408409```ruby410require "openai"411412client = OpenAI::Client.new413response = client.responses.create(414 model: "gpt-6-astra",415 input: "List files in the container and show disk usage.",416 tools: [417 {418 type: :shell,419 environment: {420 type: :container_reference,421 container_id: "cntr_08f3d96c87a585390069118b594f7481a088b16cda7d9415fe"422 }423 }424 ]425)426427puts(response.output_text)428```429430431## Attach skills432433Skills are reusable, versioned bundles that you can mount in hosted shell environments. This defines the available skills, and at shell execution time the model decides whether to invoke them.434435Use the [Skills guide](https://developers.openai.com/api/docs/guides/tools-skills) for upload and versioning details.436437Create a container with attached skills438439```bash440curl -L 'https://api.openai.com/v1/containers' \441 -H "Content-Type: application/json" \442 -H "Authorization: Bearer $OPENAI_API_KEY" \443 -d '{444 "name": "skill-container",445 "skills": [446 { "type": "skill_reference", "skill_id": "skill_4db6f1a2c9e73508b41f9da06e2c7b5f" },447 { "type": "skill_reference", "skill_id": "openai-spreadsheets", "version": "latest" }448 ]449 }'450```451452```javascript453import OpenAI from "openai";454455const client = new OpenAI();456457const container = await client.containers.create({458 name: "skill-container",459 skills: [460 {461 type: "skill_reference",462 skill_id: "skill_4db6f1a2c9e73508b41f9da06e2c7b5f",463 },464 {465 type: "skill_reference",466 skill_id: "openai-spreadsheets",467 version: "latest",468 },469 ],470});471472console.log(container.id);473```474475```python476# Replace the illustrative IDs and URLs below with your own resource values.477from openai import OpenAI478479client = OpenAI()480skill_id = "skill_123"481482container = client.containers.create(483 name="skill-container",484 skills=[485 {486 "type": "skill_reference",487 "skill_id": skill_id,488 },489 {490 "type": "skill_reference",491 "skill_id": "openai-spreadsheets",492 "version": "latest",493 },494 ],495)496497print(container.id)498```499500```go501package main502503import (504 "context"505 "fmt"506507 "github.com/openai/openai-go/v3"508 "github.com/openai/openai-go/v3/responses"509)510511func main() {512 client := openai.NewClient()513 container, err := client.Containers.New(context.Background(), openai.ContainerNewParams{514 Name: "skill-container",515 Skills: []openai.ContainerNewParamsSkillUnion{516 {OfSkillReference: &responses.SkillReferenceParam{SkillID: "skill_4db6f1a2c9e73508b41f9da06e2c7b5f"}},517 {OfSkillReference: &responses.SkillReferenceParam{SkillID: "openai-spreadsheets", Version: openai.String("latest")}},518 },519 })520 if err != nil {521 panic(err)522 }523 fmt.Println(container.ID)524}525```526527```java528import com.openai.client.OpenAIClient;529import com.openai.client.okhttp.OpenAIOkHttpClient;530import com.openai.models.containers.ContainerCreateParams;531import com.openai.models.responses.SkillReference;532533String skillId = "skill_4db6f1a2c9e73508b41f9da06e2c7b5f";534535var container =536 client537 .containers()538 .create(539 ContainerCreateParams.builder()540 .name("skill-container")541 .addSkill(SkillReference.builder().skillId(skillId).build())542 .addSkill(543 SkillReference.builder()544 .skillId("openai-spreadsheets")545 .version("latest")546 .build())547 .build());548549System.out.println(container.id());550```551552```ruby553require "openai"554555client = OpenAI::Client.new556container = client.containers.create(557 name: "skill-container",558 skills: [559 {560 type: :skill_reference,561 skill_id: "skill_4db6f1a2c9e73508b41f9da06e2c7b5f"562 },563 {564 type: :skill_reference,565 skill_id: "openai-spreadsheets",566 version: "latest"567 }568 ]569)570571puts(container.id)572```573574575## Network access576577Hosted containers don't have outbound network access by default.578579To enable it:5805811. An admin must configure your org allow list in the dashboard.5822. You must explicitly set `network_policy` on the container environment in your request.583584Shell tool with network allowlist585586```bash587curl -L 'https://api.openai.com/v1/responses' \588 -H "Authorization: Bearer $OPENAI_API_KEY" \589 -H "Content-Type: application/json" \590 -d '{591 "model": "gpt-6-astra",592 "tool_choice": "required",593 "tools": [594 {595 "type": "shell",596 "environment": {597 "type": "container_auto",598 "network_policy": {599 "type": "allowlist",600 "allowed_domains": ["pypi.org", "files.pythonhosted.org", "github.com"]601 }602 }603 }604 ],605 "input": [606 {607 "role": "user",608 "content": "In the container, pip install httpx beautifulsoup4, fetch release pages, and write /mnt/data/release_digest.md."609 }610 ]611 }'612```613614```javascript615import OpenAI from "openai";616617const client = new OpenAI();618619const response = await client.responses.create({620 model: "gpt-6-astra",621 tool_choice: "required",622 tools: [623 {624 type: "shell",625 environment: {626 type: "container_auto",627 network_policy: {628 type: "allowlist",629 allowed_domains: ["pypi.org", "files.pythonhosted.org", "github.com"],630 },631 },632 },633 ],634 input: [635 {636 role: "user",637 content:638 "In the container, pip install httpx beautifulsoup4, fetch release pages, and write /mnt/data/release_digest.md.",639 },640 ],641});642643console.log(response.output_text);644```645646```python647from openai import OpenAI648649client = OpenAI()650651response = client.responses.create(652 model="gpt-6-astra",653 tool_choice="required",654 tools=[655 {656 "type": "shell",657 "environment": {658 "type": "container_auto",659 "network_policy": {660 "type": "allowlist",661 "allowed_domains": [662 "pypi.org",663 "files.pythonhosted.org",664 "github.com",665 ],666 },667 },668 }669 ],670 input=[671 {672 "role": "user",673 "content": "In the container, pip install httpx beautifulsoup4, fetch release pages, and write /mnt/data/release_digest.md.",674 }675 ],676)677678print(response.output_text)679```680681```go682package main683684import (685 "context"686 "fmt"687688 "github.com/openai/openai-go/v3"689 "github.com/openai/openai-go/v3/responses"690)691692func main() {693 client := openai.NewClient()694 tool := responses.ToolUnionParam{OfShell: &responses.FunctionShellToolParam{695 Environment: responses.FunctionShellToolEnvironmentUnionParam{OfContainerAuto: &responses.ContainerAutoParam{696 NetworkPolicy: responses.ContainerAutoNetworkPolicyUnionParam{OfAllowlist: &responses.ContainerNetworkPolicyAllowlistParam{697 AllowedDomains: []string{"pypi.org", "files.pythonhosted.org", "github.com"},698 }},699 }},700 }}701 response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{702 Model: "gpt-6-astra",703 ToolChoice: responses.ResponseNewParamsToolChoiceUnion{OfToolChoiceMode: openai.Opt(responses.ToolChoiceOptionsRequired)},704 Tools: []responses.ToolUnionParam{tool},705 Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("In the container, pip install httpx beautifulsoup4, fetch release pages, and write /mnt/data/release_digest.md.")},706 })707 if err != nil {708 panic(err)709 }710 fmt.Println(response.OutputText())711}712```713714```java715import com.openai.client.OpenAIClient;716import com.openai.client.okhttp.OpenAIOkHttpClient;717import com.openai.models.responses.ContainerAuto;718import com.openai.models.responses.ContainerNetworkPolicyAllowlist;719import com.openai.models.responses.FunctionShellTool;720import com.openai.models.responses.ResponseCreateParams;721import com.openai.models.responses.ToolChoiceOptions;722723ResponseCreateParams params =724 ResponseCreateParams.builder()725 .model("gpt-6-astra")726 .input("Fetch release pages and write /mnt/data/release_digest.md.")727 .toolChoice(ToolChoiceOptions.REQUIRED)728 .addTool(729 FunctionShellTool.builder()730 .environment(731 ContainerAuto.builder()732 .networkPolicy(733 ContainerNetworkPolicyAllowlist.builder()734 .addAllowedDomain("pypi.org")735 .addAllowedDomain("files.pythonhosted.org")736 .addAllowedDomain("github.com")737 .build())738 .build())739 .build())740 .build();741742client.responses().create(params).output().stream()743 .flatMap(item -> item.message().stream())744 .flatMap(message -> message.content().stream())745 .flatMap(content -> content.outputText().stream())746 .forEach(text -> System.out.println(text.text()));747```748749```ruby750require "openai"751752client = OpenAI::Client.new753response = client.responses.create(754 model: "gpt-6-astra",755 input: "Fetch release pages and write /mnt/data/release_digest.md.",756 tool_choice: :required,757 tools: [758 {759 type: :shell,760 environment: {761 type: :container_auto,762 network_policy: {763 type: :allowlist,764 allowed_domains: ["pypi.org", "files.pythonhosted.org", "github.com"]765 }766 }767 }768 ]769)770771puts(response.output_text)772```773774775Allowlisting domains introduces security risks such as prompt776 injection-driven data exfiltration. Only allowlist domains you trust and that777 attackers cannot use to receive exfiltrated data. Carefully review the [Risks778 and safety](#risks-and-safety) section below before using this tool.779780## Network policy precedence781782When multiple controls are present:783784- Your org allow list defines the full set of `allowed_domains`.785- Request-level `network_policy` further restricts access.786- Requests fail if `allowed_domains` includes domains outside your org allow list.787788## Data retention and container lifecycle789790Hosted containers used by Hosted Shell and Code Interpreter may write temporary application state to the container filesystem (backed by ephemeral block storage) while the container is active. Container data is deleted when the container expires or is explicitly deleted.791792For more details on data controls, see [ZDR and data residency](https://developers.openai.com/api/docs/guides/your-data).793794### Download artifacts795796Hosted shell can produce downloadable files. Use the same container/files APIs as code interpreter to retrieve artifacts written under `/mnt/data`.797798### Additional data controls799800If you want to keep content and files ephemeral within the hosted lifecycle, you can inline files in the request and mount inline skills in the container.801802Use inline files and inline skills803804```bash805INLINE_ZIP=$(base64 -i ./csv_insights.zip)806REPORT_CSV=$(base64 -i ./report.csv)807808CONTAINER_ID=$(809 curl -sL 'https://api.openai.com/v1/containers' \810 -H "Content-Type: application/json" \811 -H "Authorization: Bearer $OPENAI_API_KEY" \812 -d '{813 "name": "inline-skill-container",814 "skills": [815 {816 "type": "inline",817 "name": "csv-insights",818 "description": "Summarize CSV files and produce a markdown report.",819 "source": {820 "type": "base64",821 "media_type": "application/zip",822 "data": "'"$INLINE_ZIP"'"823 }824 }825 ]826 }' | jq -r '.id'827)828829curl -L 'https://api.openai.com/v1/responses' \830 -H "Content-Type: application/json" \831 -H "Authorization: Bearer $OPENAI_API_KEY" \832 -d '{833 "model": "gpt-6-astra",834 "tools": [835 {836 "type": "shell",837 "environment": {838 "type": "container_reference",839 "container_id": "'"$CONTAINER_ID"'"840 }841 }842 ],843 "input": [844 {845 "role": "user",846 "content": [847 {848 "type": "input_file",849 "filename": "report.csv",850 "file_data": "data:text/csv;base64,'"${REPORT_CSV}"'"851 },852 {853 "type": "input_text",854 "text": "Use the csv-insights skill to summarize report.csv."855 }856 ]857 }858 ]859 }'860```861862```javascript863import fs from "fs";864import OpenAI from "openai";865866const client = new OpenAI();867868const inlineZip = fs869 .readFileSync("fixtures/csv_insights.zip")870 .toString("base64");871const reportCsv = fs.readFileSync("fixtures/report.csv").toString("base64");872873const container = await client.containers.create({874 name: "inline-skill-container",875 skills: [876 {877 type: "inline",878 name: "csv-insights",879 description: "Summarize CSV files and produce a markdown report.",880 source: {881 type: "base64",882 media_type: "application/zip",883 data: inlineZip,884 },885 },886 ],887});888889const response = await client.responses.create({890 model: "gpt-6-astra",891 tools: [892 {893 type: "shell",894 environment: {895 type: "container_reference",896 container_id: container.id,897 },898 },899 ],900 input: [901 {902 role: "user",903 content: [904 {905 type: "input_file",906 filename: "report.csv",907 file_data: `data:text/csv;base64,${reportCsv}`,908 },909 {910 type: "input_text",911 text: "Use the csv-insights skill to summarize report.csv.",912 },913 ],914 },915 ],916});917918console.log(response.output_text);919```920921```python922import base64923from openai import OpenAI924925client = OpenAI()926927with open("csv_insights.zip", "rb") as f:928 inline_zip = base64.b64encode(f.read()).decode("utf-8")929930with open("report.csv", "rb") as f:931 base64_string = base64.b64encode(f.read()).decode("utf-8")932933container = client.containers.create(934 name="inline-skill-container",935 skills=[936 {937 "type": "inline",938 "name": "csv-insights",939 "description": "Summarize CSV files and produce a markdown report.",940 "source": {941 "type": "base64",942 "media_type": "application/zip",943 "data": inline_zip,944 },945 }946 ],947)948949response = client.responses.create(950 model="gpt-6-astra",951 tools=[952 {953 "type": "shell",954 "environment": {955 "type": "container_reference",956 "container_id": container.id,957 },958 }959 ],960 input=[961 {962 "role": "user",963 "content": [964 {965 "type": "input_file",966 "filename": "report.csv",967 "file_data": f"data:text/csv;base64,{base64_string}",968 },969 {970 "type": "input_text",971 "text": "Use the csv-insights skill to summarize report.csv.",972 },973 ],974 }975 ],976)977978print(response.output_text)979```980981```ruby982require "base64"983require "openai"984985client = OpenAI::Client.new986inline_zip = Base64.strict_encode64(File.binread("csv_insights.zip"))987base64_string = Base64.strict_encode64(File.binread("report.csv"))988container = client.containers.create(989 name: "inline-skill-container",990 skills: [991 {992 type: :inline,993 name: "csv-insights",994 description: "Summarize CSV files and produce a markdown report.",995 source: {996 type: :base64,997 media_type: "application/zip",998 data: inline_zip999 }1000 }1001 ]1002)1003response = client.responses.create(1004 model: "gpt-6-astra",1005 tools: [1006 {1007 type: :shell,1008 environment: {1009 type: :container_reference,1010 container_id: container.id1011 }1012 }1013 ],1014 input: [1015 {1016 role: :user,1017 content: [1018 {1019 type: :input_file,1020 filename: "report.csv",1021 file_data: "data:text/csv;base64,#{base64_string}"1022 },1023 {1024 type: :input_text,1025 text: "Use the csv-insights skill to summarize report.csv."1026 }1027 ]1028 }1029 ]1030)1031puts(response.output_text)1032```103310341035For follow-up requests, pass the same `container_id` with `container_reference`. The mounted skills and existing container files remain available while the container is active.10361037### Proactively delete a container10381039You can explicitly delete the container when the work is done instead of waiting for inactivity expiration.10401041Delete a container10421043```bash1044curl -L -X DELETE 'https://api.openai.com/v1/containers/container_id' \1045 -H "Authorization: Bearer $OPENAI_API_KEY"1046```10471048```javascript1049import OpenAI from "openai";10501051const client = new OpenAI();10521053const deleted = await client.containers.delete("container_id");10541055console.log(deleted);1056```10571058```python1059# Replace the illustrative IDs and URLs below with your own resource values.1060from openai import OpenAI10611062client = OpenAI()1063container_id = "cntr_123"10641065deleted = client.containers.delete(container_id)10661067print(deleted)1068```10691070```go1071package main10721073import (1074 "context"1075 "fmt"10761077 "github.com/openai/openai-go/v3"1078)10791080func main() {1081 client := openai.NewClient()1082 if err := client.Containers.Delete(context.Background(), "container_id"); err != nil {1083 panic(err)1084 }1085 fmt.Println("Container deleted")1086}1087```10881089```java1090import com.openai.client.OpenAIClient;1091import com.openai.client.okhttp.OpenAIOkHttpClient;10921093String containerId = "container_id";10941095client.containers().delete(containerId);10961097System.out.println("Container deleted.");1098```10991100```ruby1101require "openai"11021103client = OpenAI::Client.new1104client.containers.delete("container_id")1105puts("Deleted container_id")1106```110711081109## Domain secrets11101111Use `domain_secrets` when a domain in your `allowed_domains` list requires private authorization headers, such as `Authorization: Bearer <token>`.11121113Each secret entry includes:11141115- Target domain1116- Friendly secret name1117- Secret value11181119At runtime:11201121- The model and runtime see placeholder names (for example, `$API_KEY`) instead of raw credentials.1122- The auth-translation sidecar applies raw secret values only for approved destinations.1123- Raw secret values don't persist on API servers and don't appear in model-visible context.11241125This lets the assistant call protected services while reducing leakage risk.11261127Shell tool with domain_secrets11281129```bash1130curl -L 'https://api.openai.com/v1/responses' \1131 -H "Authorization: Bearer $OPENAI_API_KEY" \1132 -H "Content-Type: application/json" \1133 -d '{1134 "model": "gpt-6-astra",1135 "input": [1136 {1137 "role": "user",1138 "content": "Use curl to call https://httpbin.org/headers with header Authorization: Bearer $API_KEY. Tell me what you see in the final text response."1139 }1140 ],1141 "tool_choice": "required",1142 "tools": [1143 {1144 "type": "shell",1145 "environment": {1146 "type": "container_auto",1147 "network_policy": {1148 "type": "allowlist",1149 "allowed_domains": ["httpbin.org"],1150 "domain_secrets": [1151 {1152 "domain": "httpbin.org",1153 "name": "API_KEY",1154 "value": "debug-secret-123"1155 }1156 ]1157 }1158 }1159 }1160 ]1161 }'1162```11631164```javascript1165import OpenAI from "openai";11661167const client = new OpenAI();11681169const response = await client.responses.create({1170 model: "gpt-6-astra",1171 input: [1172 {1173 role: "user",1174 content:1175 "Use curl to call https://httpbin.org/headers with header Authorization: Bearer $API_KEY. Tell me what you see in the final text response.",1176 },1177 ],1178 tool_choice: "required",1179 tools: [1180 {1181 type: "shell",1182 environment: {1183 type: "container_auto",1184 network_policy: {1185 type: "allowlist",1186 allowed_domains: ["httpbin.org"],1187 domain_secrets: [1188 {1189 domain: "httpbin.org",1190 name: "API_KEY",1191 value: "debug-secret-123",1192 },1193 ],1194 },1195 },1196 },1197 ],1198});11991200console.log(response.output_text);1201```12021203```python1204from openai import OpenAI12051206client = OpenAI()12071208response = client.responses.create(1209 model="gpt-6-astra",1210 input=[1211 {1212 "role": "user",1213 "content": "Use curl to call https://httpbin.org/headers with header Authorization: Bearer $API_KEY. Tell me what you see in the final text response.",1214 }1215 ],1216 tool_choice="required",1217 tools=[1218 {1219 "type": "shell",1220 "environment": {1221 "type": "container_auto",1222 "network_policy": {1223 "type": "allowlist",1224 "allowed_domains": ["httpbin.org"],1225 "domain_secrets": [1226 {1227 "domain": "httpbin.org",1228 "name": "API_KEY",1229 "value": "debug-secret-123",1230 }1231 ],1232 },1233 },1234 }1235 ],1236)12371238print(response.output_text)1239```12401241```go1242package main12431244import (1245 "context"1246 "fmt"12471248 "github.com/openai/openai-go/v3"1249 "github.com/openai/openai-go/v3/responses"1250)12511252func main() {1253 client := openai.NewClient()1254 tool := responses.ToolUnionParam{OfShell: &responses.FunctionShellToolParam{1255 Environment: responses.FunctionShellToolEnvironmentUnionParam{OfContainerAuto: &responses.ContainerAutoParam{1256 NetworkPolicy: responses.ContainerAutoNetworkPolicyUnionParam{OfAllowlist: &responses.ContainerNetworkPolicyAllowlistParam{1257 AllowedDomains: []string{"httpbin.org"},1258 DomainSecrets: []responses.ContainerNetworkPolicyDomainSecretParam{{1259 Domain: "httpbin.org",1260 Name: "API_KEY",1261 Value: "debug-secret-123",1262 }},1263 }},1264 }},1265 }}1266 response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{1267 Model: "gpt-6-astra",1268 ToolChoice: responses.ResponseNewParamsToolChoiceUnion{OfToolChoiceMode: openai.Opt(responses.ToolChoiceOptionsRequired)},1269 Tools: []responses.ToolUnionParam{tool},1270 Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Use curl to call https://httpbin.org/headers with header Authorization: Bearer $API_KEY. Tell me what you see in the final text response.")},1271 })1272 if err != nil {1273 panic(err)1274 }1275 fmt.Println(response.OutputText())1276}1277```12781279```java1280import com.openai.client.OpenAIClient;1281import com.openai.client.okhttp.OpenAIOkHttpClient;1282import com.openai.models.responses.ContainerAuto;1283import com.openai.models.responses.ContainerNetworkPolicyAllowlist;1284import com.openai.models.responses.ContainerNetworkPolicyDomainSecret;1285import com.openai.models.responses.FunctionShellTool;1286import com.openai.models.responses.ResponseCreateParams;1287import com.openai.models.responses.ToolChoiceOptions;12881289ResponseCreateParams params =1290 ResponseCreateParams.builder()1291 .model("gpt-6-astra")1292 .input(1293 "Use curl to call https://httpbin.org/status/204 with an "1294 + "Authorization: Bearer $API_KEY header. Print only the HTTP status code; "1295 + "never print request headers or secret values.")1296 .toolChoice(ToolChoiceOptions.REQUIRED)1297 .addTool(1298 FunctionShellTool.builder()1299 .environment(1300 ContainerAuto.builder()1301 .networkPolicy(1302 ContainerNetworkPolicyAllowlist.builder()1303 .addAllowedDomain("httpbin.org")1304 .addDomainSecret(1305 ContainerNetworkPolicyDomainSecret.builder()1306 .domain("httpbin.org")1307 .name("API_KEY")1308 .value(System.getenv("OPENAI_EXAMPLE_DOMAIN_SECRET"))1309 .build())1310 .build())1311 .build())1312 .build())1313 .build();13141315client.responses().create(params).output().stream()1316 .flatMap(item -> item.message().stream())1317 .flatMap(message -> message.content().stream())1318 .flatMap(content -> content.outputText().stream())1319 .forEach(text -> System.out.println(text.text()));1320```13211322```ruby1323require "openai"13241325client = OpenAI::Client.new1326response = client.responses.create(1327 model: "gpt-6-astra",1328 input: "Use curl to call https://httpbin.org/headers with an " \1329 '"Authorization: Bearer $API_KEY" header.',1330 tool_choice: :required,1331 tools: [1332 {1333 type: :shell,1334 environment: {1335 type: :container_auto,1336 network_policy: {1337 type: :allowlist,1338 allowed_domains: ["httpbin.org"],1339 domain_secrets: [1340 {1341 domain: "httpbin.org",1342 name: "API_KEY",1343 value: "debug-secret-123"1344 }1345 ]1346 }1347 }1348 }1349 ]1350)13511352puts(response.output_text)1353```135413551356## Multi-turn workflows13571358To continue work in the same hosted environment, reuse the container and pass `previous_response_id`.13591360Continue a shell workflow13611362```bash1363curl -L 'https://api.openai.com/v1/responses' \1364 -H "Content-Type: application/json" \1365 -H "Authorization: Bearer $OPENAI_API_KEY" \1366 -d '{1367 "model": "gpt-6-astra",1368 "previous_response_id": "resp_2a8e5c9174d63b0f18a4c572de9f64a1b3c76d508e12f9ab47",1369 "tools": [1370 {1371 "type": "shell",1372 "environment": {1373 "type": "container_reference",1374 "container_id": "cntr_f19c2b51e4a06793d82d54a7be0fc9154d3361ab28ce7f6041"1375 }1376 }1377 ],1378 "input": "Read /mnt/data/top5.csv and report the top candidate."1379 }'1380```13811382```javascript1383import OpenAI from "openai";13841385const client = new OpenAI();13861387const response = await client.responses.create({1388 model: "gpt-6-astra",1389 previous_response_id:1390 "resp_2a8e5c9174d63b0f18a4c572de9f64a1b3c76d508e12f9ab47",1391 tools: [1392 {1393 type: "shell",1394 environment: {1395 type: "container_reference",1396 container_id: "cntr_f19c2b51e4a06793d82d54a7be0fc9154d3361ab28ce7f6041",1397 },1398 },1399 ],1400 input: "Read /mnt/data/top5.csv and report the top candidate.",1401});14021403console.log(response.output_text);1404```14051406```python1407from openai import OpenAI14081409client = OpenAI()14101411response = client.responses.create(1412 model="gpt-6-astra",1413 previous_response_id="resp_2a8e5c9174d63b0f18a4c572de9f64a1b3c76d508e12f9ab47",1414 tools=[1415 {1416 "type": "shell",1417 "environment": {1418 "type": "container_reference",1419 "container_id": "cntr_f19c2b51e4a06793d82d54a7be0fc9154d3361ab28ce7f6041",1420 },1421 }1422 ],1423 input="Read /mnt/data/top5.csv and report the top candidate.",1424)14251426print(response.output_text)1427```14281429```go1430package main14311432import (1433 "context"1434 "fmt"14351436 "github.com/openai/openai-go/v3"1437 "github.com/openai/openai-go/v3/responses"1438)14391440func main() {1441 client := openai.NewClient()1442 tool := responses.ToolUnionParam{OfShell: &responses.FunctionShellToolParam{1443 Environment: responses.FunctionShellToolEnvironmentUnionParam{OfContainerReference: &responses.ContainerReferenceParam{ContainerID: "cntr_f19c2b51e4a06793d82d54a7be0fc9154d3361ab28ce7f6041"}},1444 }}1445 response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{1446 Model: "gpt-6-astra",1447 PreviousResponseID: openai.String("resp_2a8e5c9174d63b0f18a4c572de9f64a1b3c76d508e12f9ab47"),1448 Tools: []responses.ToolUnionParam{tool},1449 Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Read /mnt/data/top5.csv and report the top candidate.")},1450 })1451 if err != nil {1452 panic(err)1453 }1454 fmt.Println(response.OutputText())1455}1456```14571458```java1459import com.openai.client.OpenAIClient;1460import com.openai.client.okhttp.OpenAIOkHttpClient;1461import com.openai.models.responses.FunctionShellTool;1462import com.openai.models.responses.ResponseCreateParams;14631464String responseId = "resp_2a8e5c9174d63b0f18a4c572de9f64a1b3c76d508e12f9ab47";14651466String containerId = "cntr_f19c2b51e4a06793d82d54a7be0fc9154d3361ab28ce7f6041";14671468ResponseCreateParams params =1469 ResponseCreateParams.builder()1470 .model("gpt-6-astra")1471 .input("Read /mnt/data/top5.csv and report the top candidate.")1472 .previousResponseId(responseId)1473 .addTool(FunctionShellTool.builder().containerReferenceEnvironment(containerId).build())1474 .build();14751476client.responses().create(params).output().stream()1477 .flatMap(item -> item.message().stream())1478 .flatMap(message -> message.content().stream())1479 .flatMap(content -> content.outputText().stream())1480 .forEach(text -> System.out.println(text.text()));1481```14821483```ruby1484require "openai"14851486client = OpenAI::Client.new1487response = client.responses.create(1488 model: "gpt-6-astra",1489 input: "Read /mnt/data/top5.csv and report the top candidate.",1490 previous_response_id: "resp_2a8e5c9174d63b0f18a4c572de9f64a1b3c76d508e12f9ab47",1491 tools: [1492 {1493 type: :shell,1494 environment: {1495 type: :container_reference,1496 container_id: "cntr_f19c2b51e4a06793d82d54a7be0fc9154d3361ab28ce7f6041"1497 }1498 }1499 ]1500)15011502puts(response.output_text)1503```150415051506## Shell output in Responses15071508Hosted shell and local shell use the same output item types. Shell runs are represented by paired output items:15091510- `shell_call`: commands requested by the model.1511- `shell_call_output`: command output and exit outcomes.15121513Example shell_call item15141515```json1516{1517 "type": "shell_call",1518 "call_id": "call_9d14ac6f2b73485e91c0f4da6e1b27c8",1519 "action": {1520 "commands": ["ls -l"],1521 "timeout_ms": 120000,1522 "max_output_length": 40961523 },1524 "status": "in_progress"1525}1526```152715281529## Local shell mode15301531You can also run shell commands in your own local runtime by executing `shell_call` actions and sending `shell_call_output` back to the model.15321533Use this mode when you need full control over execution environment, filesystem access, or existing internal tooling.15341535Local shell request15361537```bash1538curl -L 'https://api.openai.com/v1/responses' \1539 -H "Content-Type: application/json" \1540 -H "Authorization: Bearer $OPENAI_API_KEY" \1541 -d '{1542 "model": "gpt-6-astra",1543 "instructions": "The local bash shell environment is on Mac.",1544 "input": "find me the largest pdf file in ~/Documents",1545 "tools": [{ "type": "shell", "environment": { "type": "local" } }]1546 }'1547```15481549```javascript1550import OpenAI from "openai";15511552const client = new OpenAI();15531554const response = await client.responses.create({1555 model: "gpt-6-astra",1556 instructions: "The local bash shell environment is on Mac.",1557 input: "find me the largest pdf file in ~/Documents",1558 tools: [{ type: "shell", environment: { type: "local" } }],1559});15601561console.log(response);1562```15631564```python1565from openai import OpenAI15661567client = OpenAI()15681569response = client.responses.create(1570 model="gpt-6-astra",1571 instructions="The local bash shell environment is on Mac.",1572 input="find me the largest pdf file in ~/Documents",1573 tools=[{"type": "shell", "environment": {"type": "local"}}],1574)15751576print(response)1577```15781579```go1580package main15811582import (1583 "context"1584 "fmt"15851586 "github.com/openai/openai-go/v3"1587 "github.com/openai/openai-go/v3/responses"1588)15891590func main() {1591 client := openai.NewClient()1592 tool := responses.ToolUnionParam{OfShell: &responses.FunctionShellToolParam{1593 Environment: responses.FunctionShellToolEnvironmentUnionParam{OfLocal: &responses.LocalEnvironmentParam{}},1594 }}1595 response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{1596 Model: "gpt-6-astra",1597 Instructions: openai.String("The local bash shell environment is on Mac."),1598 Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("find me the largest pdf file in ~/Documents")},1599 Tools: []responses.ToolUnionParam{tool},1600 })1601 if err != nil {1602 panic(err)1603 }1604 fmt.Println(response.Output)1605}1606```16071608```java1609import com.openai.client.OpenAIClient;1610import com.openai.client.okhttp.OpenAIOkHttpClient;1611import com.openai.core.JsonValue;1612import com.openai.models.responses.ResponseCreateParams;1613import java.util.List;1614import java.util.Map;16151616ResponseCreateParams params =1617 ResponseCreateParams.builder()1618 .model("gpt-6-astra")1619 .input("Find the largest PDF in ~/Documents.")1620 .instructions("The local shell environment is macOS.")1621 .putAdditionalBodyProperty(1622 "tools",1623 JsonValue.from(1624 List.of(Map.of("type", "shell", "environment", Map.of("type", "local")))))1625 .build();16261627client.responses().create(params).output().stream()1628 .flatMap(item -> item.shellCall().stream())1629 .flatMap(call -> call.action().commands().stream())1630 .forEach(System.out::println);1631```16321633```ruby1634require "openai"16351636client = OpenAI::Client.new1637response = client.responses.create(1638 model: "gpt-6-astra",1639 instructions: "The local shell environment is macOS.",1640 input: "Find the largest PDF in ~/Documents.",1641 tools: [1642 {1643 type: :shell,1644 environment: { type: :local }1645 }1646 ]1647)16481649puts(response.output)1650```165116521653When you receive `shell_call` output items:16541655- Execute requested commands in your runtime.1656- Capture `stdout`, `stderr`, and outcome.1657- Return results as `shell_call_output` in the next request.16581659Local shell executor example16601661```javascript1662import { exec as execCallback } from "node:child_process";1663import { promisify } from "node:util";16641665const exec = promisify(execCallback);16661667class ShellExecutor {1668 constructor(defaultTimeoutMs = 60_000) {1669 this.defaultTimeoutMs = defaultTimeoutMs;1670 }16711672 async run(cmd, timeoutMs) {1673 const timeout = timeoutMs ?? this.defaultTimeoutMs;16741675 try {1676 const { stdout, stderr } = await exec(cmd, { timeout });1677 return { stdout, stderr, exitCode: 0, timedOut: false };1678 } catch (error) {1679 const timedOut = Boolean(error?.killed) && error?.signal === "SIGTERM";1680 const exitCode = timedOut ? null : (error?.code ?? null);1681 return {1682 stdout: error?.stdout ?? "",1683 stderr: error?.stderr ?? String(error),1684 exitCode,1685 timedOut,1686 };1687 }1688 }1689}1690```16911692```python1693@dataclass1694class CmdResult:1695 stdout: str1696 stderr: str1697 exit_code: int | None1698 timed_out: bool169917001701class ShellExecutor:1702 def __init__(self, default_timeout: float = 60):1703 self.default_timeout = default_timeout17041705 def run(self, cmd: str, timeout: float | None = None) -> CmdResult:1706 t = timeout or self.default_timeout1707 p = subprocess.Popen(1708 cmd,1709 shell=True,1710 stdout=subprocess.PIPE,1711 stderr=subprocess.PIPE,1712 text=True,1713 )1714 try:1715 out, err = p.communicate(timeout=t)1716 return CmdResult(out, err, p.returncode, False)1717 except subprocess.TimeoutExpired:1718 p.kill()1719 out, err = p.communicate()1720 return CmdResult(out, err, p.returncode, True)1721```17221723```go1724package main17251726import (1727 "bytes"1728 "context"1729 "fmt"1730 "os/exec"1731 "time"1732)17331734type shellResult struct {1735 Stdout string1736 Stderr string1737 ExitCode int1738 TimedOut bool1739}17401741type shellExecutor struct {1742 DefaultTimeout time.Duration1743}17441745func (e shellExecutor) run(command string, timeout time.Duration) shellResult {1746 if timeout == 0 {1747 timeout = e.DefaultTimeout1748 }1749 ctx, cancel := context.WithTimeout(context.Background(), timeout)1750 defer cancel()1751 cmd := exec.CommandContext(ctx, "sh", "-c", command)1752 var stdout, stderr bytes.Buffer1753 cmd.Stdout = &stdout1754 cmd.Stderr = &stderr1755 err := cmd.Run()1756 result := shellResult{Stdout: stdout.String(), Stderr: stderr.String()}1757 if ctx.Err() == context.DeadlineExceeded {1758 result.TimedOut = true1759 result.ExitCode = -11760 return result1761 }1762 if err != nil {1763 if exitError, ok := err.(*exec.ExitError); ok {1764 result.ExitCode = exitError.ExitCode()1765 return result1766 }1767 if result.Stderr == "" {1768 result.Stderr = err.Error()1769 }1770 result.ExitCode = -11771 }1772 return result1773}17741775func main() {1776 executor := shellExecutor{DefaultTimeout: time.Minute}1777 fmt.Println(executor.run("printf shell-executor-ready", 0))1778}1779```17801781```ruby1782require "open3"17831784class ShellExecutor1785 Result = Data.define(:stdout, :stderr, :exit_code, :timed_out)17861787 def initialize(default_timeout: 60)1788 @default_timeout = default_timeout1789 end17901791 def run(command, timeout: @default_timeout)1792 Open3.popen3("sh", "-c", command, pgroup: true) do |stdin, stdout, stderr, wait_thread|1793 stdin.close1794 stdout_reader = Thread.new { stdout.read }1795 stderr_reader = Thread.new { stderr.read }1796 finished = wait_thread.join(timeout)1797 terminate_process_group(wait_thread) unless finished17981799 Result.new(1800 stdout: stdout_reader.value,1801 stderr: stderr_reader.value,1802 exit_code: wait_thread.value.exitstatus || -1,1803 timed_out: finished.nil?1804 )1805 end1806 end18071808 private18091810 def terminate_process_group(wait_thread)1811 Process.kill("TERM", -wait_thread.pid)1812 wait_thread.join(1)1813 Process.kill("KILL", -wait_thread.pid)1814 rescue Errno::ESRCH1815 nil1816 ensure1817 wait_thread.join1818 end1819end18201821puts(ShellExecutor.new.run("printf shell-executor-ready"))1822```182318241825Example shell_call_output payload18261827```json1828{1829 "type": "shell_call_output",1830 "call_id": "call_3ef1b8c79a4d6520f9e3ab7d41c68f25",1831 "max_output_length": 4096,1832 "output": [1833 {1834 "stdout": "...",1835 "stderr": "...",1836 "outcome": {1837 "type": "exit",1838 "exit_code": 01839 }1840 },1841 {1842 "stdout": "...",1843 "stderr": "...",1844 "outcome": {1845 "type": "timeout"1846 }1847 }1848 ]1849}1850```185118521853For legacy migration details, see the older [Local shell guide](https://developers.openai.com/api/docs/guides/tools-local-shell).18541855## Use local shell with Agents SDK18561857If you are using the [Agents SDK](https://developers.openai.com/api/docs/guides/tools#usage-in-the-agents-sdk), you can pass your own shell executor implementation to the shell tool helper.18581859Use local shell with Agents SDK18601861```javascript1862import { Agent, run, withTrace, shellTool } from "@openai/agents";18631864class LocalShell {1865 async run(action) {1866 return {1867 output: [1868 {1869 stdout: "Shell is not available. Needs to be implemented first.",1870 stderr: "",1871 outcome: {1872 type: "exit",1873 exitCode: 1,1874 },1875 },1876 ],1877 maxOutputLength: action.maxOutputLength,1878 };1879 }1880}18811882const shell = new LocalShell();18831884const agent = new Agent({1885 name: "Shell Assistant",1886 model: "gpt-6-astra",1887 instructions:1888 "You can execute shell commands to inspect the repository. Keep responses concise and include command output when helpful.",1889 tools: [1890 shellTool({1891 shell,1892 needsApproval: true,1893 onApproval: async (_ctx, _approvalItem) => {1894 return { approve: true };1895 },1896 }),1897 ],1898});18991900await withTrace("shell-tool-example", async () => {1901 const result = await run(agent, "Show the Node.js version.");1902 console.log(`\nFinal response:\n${result.finalOutput}`);1903});1904```19051906```python1907from agents import (1908 Agent,1909 Runner,1910 ShellCallOutcome,1911 ShellCommandOutput,1912 ShellCommandRequest,1913 ShellResult,1914 ShellTool,1915)191619171918class LocalShell:1919 async def __call__(self, request: ShellCommandRequest) -> ShellResult:1920 action = request.data.action1921 return ShellResult(1922 output=[1923 ShellCommandOutput(1924 command="(not executed)",1925 stdout="Shell is not available. Needs to be implemented first.",1926 stderr="",1927 outcome=ShellCallOutcome(type="exit", exit_code=1),1928 )1929 ],1930 max_output_length=action.max_output_length,1931 )193219331934shell_tool = ShellTool(1935 executor=LocalShell(),1936 needs_approval=True,1937 on_approval=lambda _ctx, _approval_item: {"approve": True},1938)19391940agent = Agent(1941 name="Shell Assistant",1942 model="gpt-6-astra",1943 instructions="You can execute shell commands to inspect the repository. Keep responses concise and include command output when helpful.",1944 tools=[shell_tool],1945)194619471948async def main():1949 result = await Runner.run(agent, input="Show the Node.js version.")1950 print(f"\nFinal response:\n{result.final_output}")195119521953if __name__ == "__main__":1954 import asyncio19551956 asyncio.run(main())1957```195819591960You can find working examples in the SDK repositories.19611962[Shell tool example - TypeScript1963196419651966 TypeScript example for the shell tool in the Agents SDK.](https://github.com/openai/openai-agents-js/blob/main/examples/tools/shell.ts)19671968[Shell tool example - Python1969197019711972 Python example for the shell tool in the Agents SDK.](https://github.com/openai/openai-agents-python/blob/main/examples/tools/shell.py)19731974## Handling common errors19751976- If a command exceeds your execution timeout, return a timeout outcome and include partial captured output.1977- If `max_output_length` is present on `shell_call`, include it in `shell_call_output`.1978- Don't rely on interactive commands; shell tool execution should be non-interactive.1979- Preserve non-zero exit outputs so the model can reason about recovery steps.19801981## Risks and safety19821983Enabling network access in the Containers API is a powerful capability, and it introduces meaningful security and data-governance risk. By default, network access isn't enabled. When enabled, outbound access should remain tightly scoped to trusted domains needed for the task.19841985Network-enabled containers can interact with third-party services and package registries. That creates risks including data leakage, prompt-injection-driven tool misuse, and accidental access beyond intended boundaries. These risks increase when policies are broad, static, or inconsistently enforced.19861987#### Understand prompt injection risks from network-retrieved content19881989Any external content fetched over the network may contain hidden instructions intended to manipulate model behavior. Treat untrusted network content as potentially adversarial, and require additional caution for actions that can modify data or systems.19901991#### Connect only to trusted destinations19921993Allow only domains you trust and actively maintain. Be cautious with intermediaries and aggregators that proxy to other services, and review their data handling and retention practices before you add them to your allowed domains list.19941995#### Build in reviews before and after requests are executed19961997Review the shell tool command and execution output, which are provided in the Responses API response. Capture requested hosts and actual outbound destinations for each session. Periodically review logs to verify access patterns match expectations, detect drift, and identify suspicious behavior.19981999#### Validate data residency and retention requirements20002001[OpenAI data controls](https://developers.openai.com/api/docs/guides/your-data) apply within OpenAI boundaries. However, data transmitted to third-party services over network connections is subject to their data retention policies. Ensure external endpoints meet your residency, retention, and compliance requirements.