( ctx context.Context, conn workspacesdk.AgentConn, args ExecuteArgs, optTimeout time.Duration, )
| 107 | } |
| 108 | |
| 109 | func executeTool( |
| 110 | ctx context.Context, |
| 111 | conn workspacesdk.AgentConn, |
| 112 | args ExecuteArgs, |
| 113 | optTimeout time.Duration, |
| 114 | ) fantasy.ToolResponse { |
| 115 | if args.Command == "" { |
| 116 | return fantasy.NewTextErrorResponse("command is required") |
| 117 | } |
| 118 | |
| 119 | // Build the environment map for the process request. |
| 120 | env := make(map[string]string, len(nonInteractiveEnvVars)+1) |
| 121 | env["CODER_CHAT_AGENT"] = "true" |
| 122 | for k, v := range nonInteractiveEnvVars { |
| 123 | env[k] = v |
| 124 | } |
| 125 | |
| 126 | background := args.RunInBackground != nil && *args.RunInBackground |
| 127 | |
| 128 | // Detect shell-style backgrounding (trailing &) and promote to |
| 129 | // background mode. Models sometimes use "cmd &" instead of the |
| 130 | // run_in_background parameter, which causes the shell to fork |
| 131 | // and exit immediately, leaving an untracked orphan process. |
| 132 | trimmed := strings.TrimSpace(args.Command) |
| 133 | if !background && strings.HasSuffix(trimmed, "&") && !strings.HasSuffix(trimmed, "&&") && !strings.HasSuffix(trimmed, "|&") { |
| 134 | background = true |
| 135 | args.Command = strings.TrimSpace(strings.TrimSuffix(trimmed, "&")) |
| 136 | } |
| 137 | |
| 138 | var workDir string |
| 139 | if args.WorkDir != nil { |
| 140 | workDir = *args.WorkDir |
| 141 | } |
| 142 | |
| 143 | if background { |
| 144 | return executeBackground(ctx, conn, args.Command, workDir, env) |
| 145 | } |
| 146 | return executeForeground(ctx, conn, args, optTimeout, workDir, env) |
| 147 | } |
| 148 | |
| 149 | // executeBackground starts a process in the background and |
| 150 | // returns immediately with the process ID. |
no test coverage detected